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:
parent
b5ba482fc2
commit
f68c77d785
|
|
@ -314,6 +314,7 @@ jobs:
|
||||||
libxkbcommon-dev \
|
libxkbcommon-dev \
|
||||||
libxrandr-dev \
|
libxrandr-dev \
|
||||||
libxss-dev \
|
libxss-dev \
|
||||||
|
libxtst-dev \
|
||||||
lld \
|
lld \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
wayland-protocols
|
wayland-protocols
|
||||||
|
|
@ -359,7 +360,7 @@ jobs:
|
||||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR" 2>&1 | tee _Build/configure.log
|
-DCMAKE_PREFIX_PATH="$Qt6_DIR" 2>&1 | tee _Build/configure.log
|
||||||
exit "${PIPESTATUS[0]}"
|
exit "${PIPESTATUS[0]}"
|
||||||
|
|
||||||
- name: Verify SDL2 backends
|
- name: Verify SDL3 backends
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
status=0
|
status=0
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[submodule "3rdparty/SDL2"]
|
[submodule "3rdparty/SDL3"]
|
||||||
path = 3rdparty/SDL2
|
path = 3rdparty/SDL3
|
||||||
url = https://github.com/libsdl-org/SDL.git
|
url = https://github.com/libsdl-org/SDL.git
|
||||||
branch = SDL2
|
branch = release-3.4.x
|
||||||
[submodule "3rdparty/magic_enum"]
|
[submodule "3rdparty/magic_enum"]
|
||||||
path = 3rdparty/magic_enum
|
path = 3rdparty/magic_enum
|
||||||
url = https://github.com/Neargye/magic_enum.git
|
url = https://github.com/Neargye/magic_enum.git
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ FetchContent_Declare(
|
||||||
FetchContent_MakeAvailable(xbyak zydis)
|
FetchContent_MakeAvailable(xbyak zydis)
|
||||||
|
|
||||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||||
add_subdirectory(SDL2)
|
add_subdirectory(SDL3)
|
||||||
|
|
||||||
add_library(Vulkan-Headers INTERFACE)
|
add_library(Vulkan-Headers INTERFACE)
|
||||||
add_library(Vulkan::Headers ALIAS Vulkan-Headers)
|
add_library(Vulkan::Headers ALIAS Vulkan-Headers)
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 4b69833bc54abf3dd3288d4aa7afbba527775e5b
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit fa2c02bb6e21974a89ea9824bc53c9932abe5f9c
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 3.12...4.3)
|
cmake_minimum_required(VERSION 3.16...4.3)
|
||||||
|
|
||||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.0)
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.0)
|
||||||
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "Minimum policy version for bundled dependencies")
|
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "Minimum policy version for bundled dependencies")
|
||||||
|
|
@ -331,7 +331,7 @@ if (KYTY_CLANG_CL)
|
||||||
set_source_files_properties(${kyty_emulator_src} PROPERTIES COMPILE_FLAGS "-Wno-pragma-pack -Wno-deprecated-declarations -D_TIMESPEC_DEFINED")
|
set_source_files_properties(${kyty_emulator_src} PROPERTIES COMPILE_FLAGS "-Wno-pragma-pack -Wno-deprecated-declarations -D_TIMESPEC_DEFINED")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(kyty_emulator_link_libraries common Vulkan::Headers spirv-tools-opt spirv-tools SDL2-static imgui xxhash FFmpeg::ffmpeg fmt::fmt nlohmann_json::nlohmann_json LibAtrac9 kyty_git_version Zydis::Zydis xbyak::xbyak)
|
set(kyty_emulator_link_libraries common Vulkan::Headers spirv-tools-opt spirv-tools SDL3::SDL3-static imgui xxhash FFmpeg::ffmpeg fmt::fmt nlohmann_json::nlohmann_json LibAtrac9 kyty_git_version Zydis::Zydis xbyak::xbyak)
|
||||||
|
|
||||||
# Linux system libraries.
|
# Linux system libraries.
|
||||||
if(LINUX)
|
if(LINUX)
|
||||||
|
|
@ -341,7 +341,6 @@ endif()
|
||||||
|
|
||||||
set(inc_headers
|
set(inc_headers
|
||||||
${KYTY_SOURCE_DIR}
|
${KYTY_SOURCE_DIR}
|
||||||
${KYTY_THIRD_PARTY_DIR}/SDL2/include
|
|
||||||
${KYTY_THIRD_PARTY_DIR}/VulkanMemoryAllocator/include
|
${KYTY_THIRD_PARTY_DIR}/VulkanMemoryAllocator/include
|
||||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Tools/include
|
${KYTY_THIRD_PARTY_DIR}/SPIRV-Tools/include
|
||||||
${KYTY_THIRD_PARTY_DIR}/SPIRV-Headers/include
|
${KYTY_THIRD_PARTY_DIR}/SPIRV-Headers/include
|
||||||
|
|
|
||||||
|
|
@ -166,15 +166,15 @@ The finished application and its runtime dependencies will be placed in
|
||||||
|
|
||||||
### Building on Linux
|
### Building on Linux
|
||||||
|
|
||||||
Install the toolchain and the libraries the bundled SDL2 needs. Without the audio, Wayland and
|
Install the toolchain and the libraries the bundled SDL3 needs. Without the audio, Wayland and
|
||||||
udev development packages SDL2 quietly configures itself without those backends, and the resulting
|
udev development packages SDL3 quietly configures itself without those backends, and the resulting
|
||||||
build has no working sound and no gamepad hotplug:
|
build has no working sound and no gamepad hotplug:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install --no-install-recommends \
|
sudo apt-get install --no-install-recommends \
|
||||||
clang lld ninja-build cmake git glslang-tools pkg-config \
|
clang lld ninja-build cmake git glslang-tools pkg-config \
|
||||||
libgl1-mesa-dev libx11-dev libxcursor-dev libxext-dev libxfixes-dev \
|
libgl1-mesa-dev libx11-dev libxcursor-dev libxext-dev libxfixes-dev \
|
||||||
libxi-dev libxrandr-dev libxss-dev libxkbcommon-dev \
|
libxi-dev libxrandr-dev libxss-dev libxtst-dev libxkbcommon-dev \
|
||||||
libasound2-dev libpulse-dev libudev-dev libdbus-1-dev libwayland-dev wayland-protocols
|
libasound2-dev libpulse-dev libudev-dev libdbus-1-dev libwayland-dev wayland-protocols
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -204,7 +204,7 @@ The CMake source root is the repository root.
|
||||||
|
|
||||||
### Building on NixOS
|
### Building on NixOS
|
||||||
|
|
||||||
A development shell provides Clang, CMake, Ninja, Qt 6, the Vulkan headers, and the SDL2 backend
|
A development shell provides Clang, CMake, Ninja, Qt 6, the Vulkan headers, and the SDL3 backend
|
||||||
libraries. Enter it and configure exactly as on other Linux distributions; the shell exports
|
libraries. Enter it and configure exactly as on other Linux distributions; the shell exports
|
||||||
`CMAKE_PREFIX_PATH` and `QT_PLUGIN_PATH`, so the `-DCMAKE_PREFIX_PATH="$Qt6_DIR"` argument is not
|
`CMAKE_PREFIX_PATH` and `QT_PLUGIN_PATH`, so the `-DCMAKE_PREFIX_PATH="$Qt6_DIR"` argument is not
|
||||||
needed:
|
needed:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
{ pkgs }:
|
{ pkgs }:
|
||||||
|
|
||||||
let
|
let
|
||||||
# Build-time dependencies. Every -dev package the bundled SDL2 probes for must
|
# Build-time dependencies for the bundled SDL3 audio, video and input backends.
|
||||||
# be listed here, otherwise SDL2 quietly drops the audio/Wayland/udev backends.
|
|
||||||
buildDeps = with pkgs; [
|
buildDeps = with pkgs; [
|
||||||
qt6.qtbase # Concurrent, Network, Widgets (launcher)
|
qt6.qtbase # Concurrent, Network, Widgets (launcher)
|
||||||
vulkan-headers
|
vulkan-headers
|
||||||
|
|
@ -18,6 +17,7 @@ let
|
||||||
libXi
|
libXi
|
||||||
libXrandr
|
libXrandr
|
||||||
libXScrnSaver
|
libXScrnSaver
|
||||||
|
libXtst
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
# Wayland
|
# Wayland
|
||||||
wayland
|
wayland
|
||||||
|
|
@ -46,6 +46,7 @@ let
|
||||||
libXi
|
libXi
|
||||||
libXrandr
|
libXrandr
|
||||||
libXScrnSaver
|
libXScrnSaver
|
||||||
|
libXtst
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
wayland
|
wayland
|
||||||
libdecor
|
libdecor
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "graphics/presentation/systemOverlay.h"
|
#include "graphics/presentation/systemOverlay.h"
|
||||||
|
|
||||||
#include "SDL.h"
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/stringUtils.h"
|
#include "common/stringUtils.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
|
|
@ -176,6 +177,7 @@ bool g_input_multiline = false;
|
||||||
bool g_input_lifecycle_active = false;
|
bool g_input_lifecycle_active = false;
|
||||||
bool g_controller_captured = false;
|
bool g_controller_captured = false;
|
||||||
OverlaySession g_session;
|
OverlaySession g_session;
|
||||||
|
SDL_Window* g_input_window = nullptr;
|
||||||
|
|
||||||
void ClearInputEvents() {
|
void ClearInputEvents() {
|
||||||
std::scoped_lock lock(g_input_mutex);
|
std::scoped_lock lock(g_input_mutex);
|
||||||
|
|
@ -212,7 +214,7 @@ void RetryVisibilityWakeup() {
|
||||||
}
|
}
|
||||||
SDL_Event event {};
|
SDL_Event event {};
|
||||||
event.type = type;
|
event.type = type;
|
||||||
if (SDL_PushEvent(&event) > 0) {
|
if (SDL_PushEvent(&event)) {
|
||||||
g_missing_visibility_wakeups--;
|
g_missing_visibility_wakeups--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +254,7 @@ void RefreshVisibility() {
|
||||||
std::scoped_lock input_lock(g_input_mutex);
|
std::scoped_lock input_lock(g_input_mutex);
|
||||||
g_visibility_updates.push_back({snapshot.session, visible, capture_controller,
|
g_visibility_updates.push_back({snapshot.session, visible, capture_controller,
|
||||||
capture_keyboard, text_input, multiline});
|
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++;
|
g_missing_visibility_wakeups++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -268,16 +270,16 @@ void OnDialogVisibilityChanged(bool, uint64_t) {
|
||||||
|
|
||||||
uint32_t ExternalKeyStatus(SDL_Keymod modifiers, bool character_valid) {
|
uint32_t ExternalKeyStatus(SDL_Keymod modifiers, bool character_valid) {
|
||||||
uint32_t status = 0x00000001 | (character_valid ? 0x00000002 : 0);
|
uint32_t status = 0x00000001 | (character_valid ? 0x00000002 : 0);
|
||||||
if ((modifiers & KMOD_LCTRL) != 0) status |= 0x00000100;
|
if ((modifiers & SDL_KMOD_LCTRL) != 0) status |= 0x00000100;
|
||||||
if ((modifiers & KMOD_LSHIFT) != 0) status |= 0x00000200;
|
if ((modifiers & SDL_KMOD_LSHIFT) != 0) status |= 0x00000200;
|
||||||
if ((modifiers & KMOD_LALT) != 0) status |= 0x00000400;
|
if ((modifiers & SDL_KMOD_LALT) != 0) status |= 0x00000400;
|
||||||
if ((modifiers & KMOD_LGUI) != 0) status |= 0x00000800;
|
if ((modifiers & SDL_KMOD_LGUI) != 0) status |= 0x00000800;
|
||||||
if ((modifiers & KMOD_RCTRL) != 0) status |= 0x00001000;
|
if ((modifiers & SDL_KMOD_RCTRL) != 0) status |= 0x00001000;
|
||||||
if ((modifiers & KMOD_RSHIFT) != 0) status |= 0x00002000;
|
if ((modifiers & SDL_KMOD_RSHIFT) != 0) status |= 0x00002000;
|
||||||
if ((modifiers & KMOD_RALT) != 0) status |= 0x00004000;
|
if ((modifiers & SDL_KMOD_RALT) != 0) status |= 0x00004000;
|
||||||
if ((modifiers & KMOD_RGUI) != 0) status |= 0x00008000;
|
if ((modifiers & SDL_KMOD_RGUI) != 0) status |= 0x00008000;
|
||||||
if ((modifiers & KMOD_NUM) != 0) status |= 0x00010000;
|
if ((modifiers & SDL_KMOD_NUM) != 0) status |= 0x00010000;
|
||||||
if ((modifiers & KMOD_CAPS) != 0) status |= 0x00020000;
|
if ((modifiers & SDL_KMOD_CAPS) != 0) status |= 0x00020000;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,14 +394,14 @@ float AlignmentPivot(Ime::Alignment alignment) {
|
||||||
|
|
||||||
ImGuiKey ControllerButtonToKey(int button) {
|
ImGuiKey ControllerButtonToKey(int button) {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case SDL_CONTROLLER_BUTTON_A: return ImGuiKey_GamepadFaceDown;
|
case SDL_GAMEPAD_BUTTON_SOUTH: return ImGuiKey_GamepadFaceDown;
|
||||||
case SDL_CONTROLLER_BUTTON_B: return ImGuiKey_GamepadFaceRight;
|
case SDL_GAMEPAD_BUTTON_EAST: return ImGuiKey_GamepadFaceRight;
|
||||||
case SDL_CONTROLLER_BUTTON_X: return ImGuiKey_GamepadFaceLeft;
|
case SDL_GAMEPAD_BUTTON_WEST: return ImGuiKey_GamepadFaceLeft;
|
||||||
case SDL_CONTROLLER_BUTTON_Y: return ImGuiKey_GamepadFaceUp;
|
case SDL_GAMEPAD_BUTTON_NORTH: return ImGuiKey_GamepadFaceUp;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return ImGuiKey_GamepadDpadLeft;
|
case SDL_GAMEPAD_BUTTON_DPAD_LEFT: return ImGuiKey_GamepadDpadLeft;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return ImGuiKey_GamepadDpadRight;
|
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: return ImGuiKey_GamepadDpadRight;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: return ImGuiKey_GamepadDpadUp;
|
case SDL_GAMEPAD_BUTTON_DPAD_UP: return ImGuiKey_GamepadDpadUp;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return ImGuiKey_GamepadDpadDown;
|
case SDL_GAMEPAD_BUTTON_DPAD_DOWN: return ImGuiKey_GamepadDpadDown;
|
||||||
default: return ImGuiKey_None;
|
default: return ImGuiKey_None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -415,9 +417,11 @@ void CheckVulkanResult(VkResult result) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void InitializeSystemOverlayInput() {
|
void InitializeSystemOverlayInput(SDL_Window* window) {
|
||||||
|
EXIT_IF(window == nullptr);
|
||||||
const Uint32 type = SDL_RegisterEvents(1);
|
const Uint32 type = SDL_RegisterEvents(1);
|
||||||
EXIT_IF(type == static_cast<Uint32>(-1));
|
EXIT_IF(type == static_cast<Uint32>(-1));
|
||||||
|
g_input_window = window;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(g_visibility_mutex);
|
std::scoped_lock lock(g_visibility_mutex);
|
||||||
g_input_lifecycle_active = true;
|
g_input_lifecycle_active = true;
|
||||||
|
|
@ -453,9 +457,10 @@ void ShutdownSystemOverlayInput() {
|
||||||
g_input_controller = false;
|
g_input_controller = false;
|
||||||
g_input_keyboard = false;
|
g_input_keyboard = false;
|
||||||
g_input_multiline = false;
|
g_input_multiline = false;
|
||||||
if (SDL_IsTextInputActive() == SDL_TRUE) {
|
if (g_input_window != nullptr && SDL_TextInputActive(g_input_window)) {
|
||||||
SDL_StopTextInput();
|
SDL_StopTextInput(g_input_window);
|
||||||
}
|
}
|
||||||
|
g_input_window = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept {
|
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept {
|
||||||
|
|
@ -487,13 +492,13 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
|
||||||
g_input_keyboard = update.capture_keyboard;
|
g_input_keyboard = update.capture_keyboard;
|
||||||
g_input_multiline = update.multiline;
|
g_input_multiline = update.multiline;
|
||||||
if (update.text_input) {
|
if (update.text_input) {
|
||||||
SDL_StartTextInput();
|
SDL_StartTextInput(g_input_window);
|
||||||
} else if (SDL_IsTextInputActive() == SDL_TRUE) {
|
} else if (SDL_TextInputActive(g_input_window)) {
|
||||||
SDL_StopTextInput();
|
SDL_StopTextInput(g_input_window);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
|
if (event.type == SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
if (g_input_active && g_input_controller) {
|
if (g_input_active && g_input_controller) {
|
||||||
QueueInput({InputKind::ResetController, g_input_session, 0, 0.0f, 0.0f});
|
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 auto session = g_input_session;
|
||||||
const uint64_t generation = session.generation;
|
const uint64_t generation = session.generation;
|
||||||
const bool controller_event = event.type == SDL_CONTROLLERBUTTONDOWN ||
|
const bool controller_event = event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ||
|
||||||
event.type == SDL_CONTROLLERBUTTONUP ||
|
event.type == SDL_EVENT_GAMEPAD_BUTTON_UP ||
|
||||||
event.type == SDL_CONTROLLERAXISMOTION;
|
event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION;
|
||||||
if (controller_event && !g_input_controller) {
|
if (controller_event && !g_input_controller) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const bool keyboard_event = event.type == SDL_TEXTINPUT || event.type == SDL_TEXTEDITING ||
|
const bool keyboard_event = event.type == SDL_EVENT_TEXT_INPUT || event.type == SDL_EVENT_TEXT_EDITING ||
|
||||||
event.type == SDL_KEYDOWN || event.type == SDL_KEYUP;
|
event.type == SDL_EVENT_KEY_DOWN || event.type == SDL_EVENT_KEY_UP;
|
||||||
if (keyboard_event && !g_input_keyboard) {
|
if (keyboard_event && !g_input_keyboard) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (keyboard_event && session.kind == OverlayKind::Error) {
|
if (keyboard_event && session.kind == OverlayKind::Error) {
|
||||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0 &&
|
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
|
||||||
(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER)) {
|
(event.key.key == SDLK_RETURN || event.key.key == SDLK_KP_ENTER)) {
|
||||||
ErrorDialog::HostAccept(generation);
|
ErrorDialog::HostAccept(generation);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_TEXTINPUT: {
|
case SDL_EVENT_TEXT_INPUT: {
|
||||||
const auto text = Utf8ToUtf16(event.text.text);
|
const auto text = Utf8ToUtf16(event.text.text);
|
||||||
if (!text.empty()) {
|
if (!text.empty()) {
|
||||||
auto input = MakeExternalInput(Ime::ExternalAction::Text, g_last_external_keycode,
|
auto input = MakeExternalInput(Ime::ExternalAction::Text, g_last_external_keycode,
|
||||||
|
|
@ -535,26 +540,24 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_TEXTEDITING: return true;
|
case SDL_EVENT_TEXT_EDITING: return true;
|
||||||
case SDL_KEYDOWN: {
|
case SDL_EVENT_KEY_DOWN: {
|
||||||
g_last_external_keycode = static_cast<uint16_t>(event.key.keysym.scancode);
|
g_last_external_keycode = static_cast<uint16_t>(event.key.scancode);
|
||||||
g_last_external_status =
|
g_last_external_status = ExternalKeyStatus(event.key.mod, false);
|
||||||
ExternalKeyStatus(static_cast<SDL_Keymod>(event.key.keysym.mod), false);
|
|
||||||
auto action = Ime::ExternalAction::Text;
|
auto action = Ime::ExternalAction::Text;
|
||||||
bool queue = true;
|
bool queue = true;
|
||||||
if (event.key.keysym.sym == SDLK_BACKSPACE) {
|
if (event.key.key == SDLK_BACKSPACE) {
|
||||||
action = Ime::ExternalAction::Backspace;
|
action = Ime::ExternalAction::Backspace;
|
||||||
} else if (event.key.keysym.sym == SDLK_LEFT) {
|
} else if (event.key.key == SDLK_LEFT) {
|
||||||
action = Ime::ExternalAction::MoveLeft;
|
action = Ime::ExternalAction::MoveLeft;
|
||||||
} else if (event.key.keysym.sym == SDLK_RIGHT) {
|
} else if (event.key.key == SDLK_RIGHT) {
|
||||||
action = Ime::ExternalAction::MoveRight;
|
action = Ime::ExternalAction::MoveRight;
|
||||||
} else if (event.key.keysym.sym == SDLK_ESCAPE) {
|
} else if (event.key.key == SDLK_ESCAPE) {
|
||||||
action = Ime::ExternalAction::Cancel;
|
action = Ime::ExternalAction::Cancel;
|
||||||
} else if (event.key.keysym.sym == SDLK_RETURN ||
|
} else if (event.key.key == SDLK_RETURN || event.key.key == SDLK_KP_ENTER) {
|
||||||
event.key.keysym.sym == SDLK_KP_ENTER) {
|
|
||||||
action =
|
action =
|
||||||
g_input_multiline ? Ime::ExternalAction::Newline : Ime::ExternalAction::Accept;
|
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;
|
action = Ime::ExternalAction::None;
|
||||||
} else {
|
} else {
|
||||||
queue = false;
|
queue = false;
|
||||||
|
|
@ -566,28 +569,28 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_KEYUP: return true;
|
case SDL_EVENT_KEY_UP: return true;
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||||
case SDL_CONTROLLERBUTTONUP:
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||||
QueueInput({InputKind::Button, session, event.cbutton.button,
|
QueueInput({InputKind::Button, session, event.gbutton.button,
|
||||||
event.type == SDL_CONTROLLERBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ? 1.0f : 0.0f, 0.0f});
|
||||||
return true;
|
return true;
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||||
QueueInput({InputKind::Axis, session, event.caxis.axis,
|
QueueInput({InputKind::Axis, session, event.gaxis.axis,
|
||||||
static_cast<float>(event.caxis.value), 0.0f});
|
static_cast<float>(event.gaxis.value), 0.0f});
|
||||||
return true;
|
return true;
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_EVENT_MOUSE_MOTION:
|
||||||
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.motion.x),
|
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.motion.x),
|
||||||
static_cast<float>(event.motion.y)});
|
static_cast<float>(event.motion.y)});
|
||||||
return true;
|
return true;
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
case SDL_MOUSEBUTTONUP:
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||||
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.button.x),
|
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.button.x),
|
||||||
static_cast<float>(event.button.y)});
|
static_cast<float>(event.button.y)});
|
||||||
QueueInput({InputKind::MouseButton, session, event.button.button,
|
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;
|
return true;
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_EVENT_MOUSE_WHEEL:
|
||||||
QueueInput({InputKind::MouseWheel, session, 0, static_cast<float>(event.wheel.x),
|
QueueInput({InputKind::MouseWheel, session, 0, static_cast<float>(event.wheel.x),
|
||||||
static_cast<float>(event.wheel.y)});
|
static_cast<float>(event.wheel.y)});
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -681,9 +684,9 @@ struct SystemOverlay::Impl {
|
||||||
case InputKind::Button: {
|
case InputKind::Button: {
|
||||||
const bool down = event.x != 0.0f;
|
const bool down = event.x != 0.0f;
|
||||||
if (session.kind == OverlayKind::Ime && down) {
|
if (session.kind == OverlayKind::Ime && down) {
|
||||||
if (event.id == SDL_CONTROLLER_BUTTON_B) {
|
if (event.id == SDL_GAMEPAD_BUTTON_EAST) {
|
||||||
Ime::HostCancel(session.generation);
|
Ime::HostCancel(session.generation);
|
||||||
} else if (event.id == SDL_CONTROLLER_BUTTON_Y) {
|
} else if (event.id == SDL_GAMEPAD_BUTTON_NORTH) {
|
||||||
Ime::HostBackspace(session.generation);
|
Ime::HostBackspace(session.generation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -695,19 +698,19 @@ struct SystemOverlay::Impl {
|
||||||
}
|
}
|
||||||
case InputKind::Axis: {
|
case InputKind::Axis: {
|
||||||
const float value = event.x / (event.x < 0.0f ? 32768.0f : 32767.0f);
|
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,
|
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickLeft, value < -0.25f,
|
||||||
std::max(-value, 0.0f));
|
std::max(-value, 0.0f));
|
||||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, value > 0.25f,
|
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, value > 0.25f,
|
||||||
std::max(value, 0.0f));
|
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,
|
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickUp, value < -0.25f,
|
||||||
std::max(-value, 0.0f));
|
std::max(-value, 0.0f));
|
||||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown, value > 0.25f,
|
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown, value > 0.25f,
|
||||||
std::max(value, 0.0f));
|
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;
|
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;
|
right_stick.y = std::abs(value) > 0.2f ? value : 0.0f;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
union SDL_Event;
|
union SDL_Event;
|
||||||
|
struct SDL_Window;
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
|
@ -17,7 +18,7 @@ struct SystemOverlayVisualState {
|
||||||
uint64_t revision;
|
uint64_t revision;
|
||||||
};
|
};
|
||||||
|
|
||||||
void InitializeSystemOverlayInput();
|
void InitializeSystemOverlayInput(SDL_Window* window);
|
||||||
void ShutdownSystemOverlayInput();
|
void ShutdownSystemOverlayInput();
|
||||||
bool ProcessSystemOverlayInput(const SDL_Event& event);
|
bool ProcessSystemOverlayInput(const SDL_Event& event);
|
||||||
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept;
|
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
#include "graphics/presentation/window/hostInput.h"
|
#include "graphics/presentation/window/hostInput.h"
|
||||||
|
|
||||||
#include "SDL_error.h"
|
#include <SDL3/SDL.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 "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
|
@ -80,6 +75,7 @@ struct MouseJoystickState {
|
||||||
};
|
};
|
||||||
|
|
||||||
MouseJoystickState g_mouse;
|
MouseJoystickState g_mouse;
|
||||||
|
SDL_Window* g_mouse_window = nullptr;
|
||||||
|
|
||||||
std::size_t ControlFromName(std::string_view name) {
|
std::size_t ControlFromName(std::string_view name) {
|
||||||
const auto info = std::find_if(CONTROL_INFO.begin(), CONTROL_INFO.end(),
|
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_LEFT: return Controller::PAD_BUTTON_LEFT;
|
||||||
case SDLK_DOWN: return Controller::PAD_BUTTON_DOWN;
|
case SDLK_DOWN: return Controller::PAD_BUTTON_DOWN;
|
||||||
case SDLK_RIGHT: return Controller::PAD_BUTTON_RIGHT;
|
case SDLK_RIGHT: return Controller::PAD_BUTTON_RIGHT;
|
||||||
case SDLK_j: return Controller::PAD_BUTTON_CROSS;
|
case SDLK_J: return Controller::PAD_BUTTON_CROSS;
|
||||||
case SDLK_i: return Controller::PAD_BUTTON_TRIANGLE;
|
case SDLK_I: return Controller::PAD_BUTTON_TRIANGLE;
|
||||||
case SDLK_k: return Controller::PAD_BUTTON_SQUARE;
|
case SDLK_K: return Controller::PAD_BUTTON_SQUARE;
|
||||||
case SDLK_l: return Controller::PAD_BUTTON_CIRCLE;
|
case SDLK_L: return Controller::PAD_BUTTON_CIRCLE;
|
||||||
case SDLK_q: return Controller::PAD_BUTTON_L1;
|
case SDLK_Q: return Controller::PAD_BUTTON_L1;
|
||||||
case SDLK_e: return Controller::PAD_BUTTON_R1;
|
case SDLK_E: return Controller::PAD_BUTTON_R1;
|
||||||
case SDLK_z: return Controller::PAD_BUTTON_L2;
|
case SDLK_Z: return Controller::PAD_BUTTON_L2;
|
||||||
case SDLK_c: return Controller::PAD_BUTTON_R2;
|
case SDLK_C: return Controller::PAD_BUTTON_R2;
|
||||||
case SDLK_LSHIFT: return Controller::PAD_BUTTON_L3;
|
case SDLK_LSHIFT: return Controller::PAD_BUTTON_L3;
|
||||||
case SDLK_LCTRL: return Controller::PAD_BUTTON_R3;
|
case SDLK_LCTRL: return Controller::PAD_BUTTON_R3;
|
||||||
case SDLK_RETURN:
|
case SDLK_RETURN:
|
||||||
|
|
@ -284,35 +280,35 @@ void DefaultKeyboardInput(int key_code, bool down) {
|
||||||
switch (NormalizeKey(static_cast<SDL_Keycode>(key_code))) {
|
switch (NormalizeKey(static_cast<SDL_Keycode>(key_code))) {
|
||||||
case SDLK_BACKSPACE: SetTouchPad(0.25f, down); return;
|
case SDLK_BACKSPACE: SetTouchPad(0.25f, down); return;
|
||||||
case SDLK_TAB: SetTouchPad(0.75f, down); return;
|
case SDLK_TAB: SetTouchPad(0.75f, down); return;
|
||||||
case SDLK_a:
|
case SDLK_A:
|
||||||
left.left = down;
|
left.left = down;
|
||||||
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
|
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
|
||||||
return;
|
return;
|
||||||
case SDLK_d:
|
case SDLK_D:
|
||||||
left.right = down;
|
left.right = down;
|
||||||
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
|
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
|
||||||
return;
|
return;
|
||||||
case SDLK_w:
|
case SDLK_W:
|
||||||
left.up = down;
|
left.up = down;
|
||||||
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
|
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
|
||||||
return;
|
return;
|
||||||
case SDLK_s:
|
case SDLK_S:
|
||||||
left.down = down;
|
left.down = down;
|
||||||
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
|
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
|
||||||
return;
|
return;
|
||||||
case SDLK_f:
|
case SDLK_F:
|
||||||
right.left = down;
|
right.left = down;
|
||||||
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
|
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
|
||||||
return;
|
return;
|
||||||
case SDLK_h:
|
case SDLK_H:
|
||||||
right.right = down;
|
right.right = down;
|
||||||
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
|
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
|
||||||
return;
|
return;
|
||||||
case SDLK_t:
|
case SDLK_T:
|
||||||
right.up = down;
|
right.up = down;
|
||||||
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
|
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
|
||||||
return;
|
return;
|
||||||
case SDLK_g:
|
case SDLK_G:
|
||||||
right.down = down;
|
right.down = down;
|
||||||
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
|
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
|
||||||
return;
|
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 distance = std::hypot(delta_x, delta_y);
|
||||||
const double scale =
|
const double scale =
|
||||||
std::clamp(distance * GetInputMap().MouseSensitivity() + 16.0, 64.0, 128.0) / distance;
|
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);
|
return std::clamp(128 + static_cast<int>(std::lround(delta * scale)), 0, 255);
|
||||||
};
|
};
|
||||||
Controller::SetRightStick(Controller::HOST_INPUT_CONTROLLER_ID, map_axis(delta_x),
|
Controller::SetRightStick(Controller::HOST_INPUT_CONTROLLER_ID, map_axis(delta_x),
|
||||||
|
|
@ -340,16 +336,45 @@ void CenterMouseStick() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetRelativeMouseMode(bool enabled) {
|
bool SetRelativeMouseMode(bool enabled) {
|
||||||
if (SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE) == 0) {
|
if (SDL_SetWindowRelativeMouseMode(g_mouse_window, enabled)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
LOGF("Mouse-to-joystick relative mode failed: %s\n", SDL_GetError());
|
LOGF("Mouse-to-joystick relative mode failed: %s\n", SDL_GetError());
|
||||||
return false;
|
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
|
} // namespace
|
||||||
|
|
||||||
void HostInputInit() {
|
void HostInputInit(SDL_Window* window) {
|
||||||
GetInputMap();
|
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) {
|
void HostInputKey(int key_code, bool down) {
|
||||||
|
|
@ -380,51 +405,33 @@ void HostInputToggleMouseToJoystick() {
|
||||||
if (!SetRelativeMouseMode(true)) {
|
if (!SetRelativeMouseMode(true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int ignored_x = 0;
|
g_mouse.enabled = true;
|
||||||
int ignored_y = 0;
|
|
||||||
SDL_GetRelativeMouseState(&ignored_x, &ignored_y);
|
|
||||||
g_mouse.enabled = true;
|
|
||||||
g_mouse.next_poll = SDL_GetTicks64() + MOUSE_POLL_INTERVAL_MS;
|
|
||||||
LOGF("Mouse to right stick: enabled (F7 to release)\n");
|
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) {
|
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();
|
CenterMouseStick();
|
||||||
if (SDL_WaitEvent(event) == 0) {
|
has_event = SDL_WaitEvent(event);
|
||||||
|
if (!has_event) {
|
||||||
EXIT("%s\n", SDL_GetError());
|
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());
|
if (has_event && event->type == SDL_EVENT_WINDOW_FOCUS_LOST &&
|
||||||
SDL_ClearError();
|
event->window.windowID == SDL_GetWindowID(g_mouse_window)) {
|
||||||
if (SDL_WaitEventTimeout(event, timeout_ms) != 0) {
|
g_mouse.next_poll = 0;
|
||||||
return true;
|
CenterMouseStick();
|
||||||
}
|
}
|
||||||
if (SDL_GetError()[0] != '\0') {
|
return has_event;
|
||||||
EXIT("%s\n", SDL_GetError());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct SDL_Window;
|
||||||
union SDL_Event;
|
union SDL_Event;
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
void HostInputInit();
|
void HostInputInit(SDL_Window* window);
|
||||||
|
void HostInputShutdown();
|
||||||
void HostInputKey(int key_code, bool down);
|
void HostInputKey(int key_code, bool down);
|
||||||
void HostInputMouseButton(uint8_t mouse_button, bool down);
|
void HostInputMouseButton(uint8_t mouse_button, bool down);
|
||||||
void HostInputToggleMouseToJoystick();
|
void HostInputToggleMouseToJoystick();
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,9 @@ void Swapchain::Recreate(bool surface_lost) {
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// Surface recreation goes through SDL_Vulkan_CreateSurface, which touches the
|
// Surface recreation goes through SDL_Vulkan_CreateSurface, which touches the
|
||||||
// window's view/layer and must run on the main thread on macOS.
|
// 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
|
#else
|
||||||
m_window.RecreateSurface();
|
m_window.RecreateSurface();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,6 @@
|
||||||
#include "SDL.h"
|
#include <SDL3/SDL.h>
|
||||||
#include "SDL_error.h"
|
#include <SDL3/SDL_vulkan.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 "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
|
|
@ -700,25 +686,13 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void VulkanGetExtensions(SDL_Window* window, VulkanExtensions& r) {
|
static void VulkanGetExtensions(VulkanExtensions& r) {
|
||||||
EXIT_IF(window == nullptr);
|
|
||||||
|
|
||||||
uint32_t required_extensions_count = 0;
|
uint32_t required_extensions_count = 0;
|
||||||
|
|
||||||
auto sdl_result = SDL_Vulkan_GetInstanceExtensions(window, &required_extensions_count, nullptr);
|
const char* const* extensions = SDL_Vulkan_GetInstanceExtensions(&required_extensions_count);
|
||||||
|
EXIT_NOT_IMPLEMENTED(extensions == nullptr);
|
||||||
EXIT_NOT_IMPLEMENTED(sdl_result == SDL_FALSE);
|
|
||||||
EXIT_NOT_IMPLEMENTED(required_extensions_count == 0);
|
EXIT_NOT_IMPLEMENTED(required_extensions_count == 0);
|
||||||
|
r.required_extensions.assign(extensions, extensions + required_extensions_count);
|
||||||
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.available_extensions =
|
r.available_extensions =
|
||||||
EnumerateVulkan<vk::ExtensionProperties>( // @suppress("Ambiguous problem")
|
EnumerateVulkan<vk::ExtensionProperties>( // @suppress("Ambiguous problem")
|
||||||
|
|
@ -899,7 +873,7 @@ void WindowContext::CreateVulkan() {
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(get_instance_proc_addr);
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(get_instance_proc_addr);
|
||||||
|
|
||||||
VulkanExtensions r;
|
VulkanExtensions r;
|
||||||
VulkanGetExtensions(window, r);
|
VulkanGetExtensions(r);
|
||||||
VulkanCheckInstanceVersion();
|
VulkanCheckInstanceVersion();
|
||||||
|
|
||||||
vk::ApplicationInfo app_info {};
|
vk::ApplicationInfo app_info {};
|
||||||
|
|
@ -986,8 +960,8 @@ void WindowContext::CreateVulkan() {
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::SurfaceKHR::CType native_surface = VK_NULL_HANDLE;
|
vk::SurfaceKHR::CType native_surface = VK_NULL_HANDLE;
|
||||||
if (SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
|
if (!SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
|
||||||
&native_surface) == SDL_FALSE) {
|
nullptr, &native_surface)) {
|
||||||
EXIT("Could not create a Vulkan surface");
|
EXIT("Could not create a Vulkan surface");
|
||||||
}
|
}
|
||||||
surface = native_surface;
|
surface = native_surface;
|
||||||
|
|
@ -1100,8 +1074,8 @@ void WindowContext::RecreateSurface() {
|
||||||
surface = nullptr;
|
surface = nullptr;
|
||||||
}
|
}
|
||||||
vk::SurfaceKHR::CType native_surface = VK_NULL_HANDLE;
|
vk::SurfaceKHR::CType native_surface = VK_NULL_HANDLE;
|
||||||
if (SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
|
if (!SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
|
||||||
&native_surface) == SDL_FALSE) {
|
nullptr, &native_surface)) {
|
||||||
EXIT("Could not recreate the Vulkan surface: %s\n", SDL_GetError());
|
EXIT("Could not recreate the Vulkan surface: %s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
surface = native_surface;
|
surface = native_surface;
|
||||||
|
|
@ -1137,7 +1111,7 @@ WindowContext::~WindowContext() {
|
||||||
SDL_DestroyWindow(window);
|
SDL_DestroyWindow(window);
|
||||||
window = nullptr;
|
window = nullptr;
|
||||||
}
|
}
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);
|
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,7 @@
|
||||||
#include "graphics/presentation/window.h"
|
#include "graphics/presentation/window.h"
|
||||||
|
|
||||||
#include "SDL.h"
|
#include <SDL3/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 "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
|
|
@ -72,39 +56,39 @@ struct EventKeyboard {
|
||||||
|
|
||||||
static uint32_t ControllerButtonToPadButton(int button) {
|
static uint32_t ControllerButtonToPadButton(int button) {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case SDL_CONTROLLER_BUTTON_A: return Controller::PAD_BUTTON_CROSS;
|
case SDL_GAMEPAD_BUTTON_SOUTH: return Controller::PAD_BUTTON_CROSS;
|
||||||
case SDL_CONTROLLER_BUTTON_B: return Controller::PAD_BUTTON_CIRCLE;
|
case SDL_GAMEPAD_BUTTON_EAST: return Controller::PAD_BUTTON_CIRCLE;
|
||||||
case SDL_CONTROLLER_BUTTON_X: return Controller::PAD_BUTTON_SQUARE;
|
case SDL_GAMEPAD_BUTTON_WEST: return Controller::PAD_BUTTON_SQUARE;
|
||||||
case SDL_CONTROLLER_BUTTON_Y: return Controller::PAD_BUTTON_TRIANGLE;
|
case SDL_GAMEPAD_BUTTON_NORTH: return Controller::PAD_BUTTON_TRIANGLE;
|
||||||
case SDL_CONTROLLER_BUTTON_START: return Controller::PAD_BUTTON_OPTIONS;
|
case SDL_GAMEPAD_BUTTON_START: return Controller::PAD_BUTTON_OPTIONS;
|
||||||
case SDL_CONTROLLER_BUTTON_LEFTSTICK: return Controller::PAD_BUTTON_L3;
|
case SDL_GAMEPAD_BUTTON_LEFT_STICK: return Controller::PAD_BUTTON_L3;
|
||||||
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return Controller::PAD_BUTTON_R3;
|
case SDL_GAMEPAD_BUTTON_RIGHT_STICK: return Controller::PAD_BUTTON_R3;
|
||||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return Controller::PAD_BUTTON_L1;
|
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: return Controller::PAD_BUTTON_L1;
|
||||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return Controller::PAD_BUTTON_R1;
|
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: return Controller::PAD_BUTTON_R1;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: return Controller::PAD_BUTTON_UP;
|
case SDL_GAMEPAD_BUTTON_DPAD_UP: return Controller::PAD_BUTTON_UP;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return Controller::PAD_BUTTON_DOWN;
|
case SDL_GAMEPAD_BUTTON_DPAD_DOWN: return Controller::PAD_BUTTON_DOWN;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return Controller::PAD_BUTTON_LEFT;
|
case SDL_GAMEPAD_BUTTON_DPAD_LEFT: return Controller::PAD_BUTTON_LEFT;
|
||||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return Controller::PAD_BUTTON_RIGHT;
|
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: return Controller::PAD_BUTTON_RIGHT;
|
||||||
case SDL_CONTROLLER_BUTTON_TOUCHPAD: return Controller::PAD_BUTTON_TOUCH_PAD;
|
case SDL_GAMEPAD_BUTTON_TOUCHPAD: return Controller::PAD_BUTTON_TOUCH_PAD;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Controller::Axis ControllerAxisFromSdl(int axis_id) {
|
static Controller::Axis ControllerAxisFromSdl(int axis_id) {
|
||||||
switch (axis_id) {
|
switch (axis_id) {
|
||||||
case SDL_CONTROLLER_AXIS_LEFTX: return Controller::Axis::LeftX;
|
case SDL_GAMEPAD_AXIS_LEFTX: return Controller::Axis::LeftX;
|
||||||
case SDL_CONTROLLER_AXIS_LEFTY: return Controller::Axis::LeftY;
|
case SDL_GAMEPAD_AXIS_LEFTY: return Controller::Axis::LeftY;
|
||||||
case SDL_CONTROLLER_AXIS_RIGHTX: return Controller::Axis::RightX;
|
case SDL_GAMEPAD_AXIS_RIGHTX: return Controller::Axis::RightX;
|
||||||
case SDL_CONTROLLER_AXIS_RIGHTY: return Controller::Axis::RightY;
|
case SDL_GAMEPAD_AXIS_RIGHTY: return Controller::Axis::RightY;
|
||||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT: return Controller::Axis::TriggerLeft;
|
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: return Controller::Axis::TriggerLeft;
|
||||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return Controller::Axis::TriggerRight;
|
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: return Controller::Axis::TriggerRight;
|
||||||
default: return Controller::Axis::AxisMax;
|
default: return Controller::Axis::AxisMax;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ControllerAxisIsTrigger(int axis_id) {
|
static bool ControllerAxisIsTrigger(int axis_id) {
|
||||||
return axis_id == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
|
return axis_id == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ||
|
||||||
axis_id == SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
|
axis_id == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ControllerAxisValueFromSdl(int axis_id, int axis_value) {
|
static int ControllerAxisValueFromSdl(int axis_id, int axis_value) {
|
||||||
|
|
@ -165,22 +149,6 @@ struct EventController {
|
||||||
double timestamp_seconds;
|
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 {
|
namespace {
|
||||||
|
|
||||||
std::unique_ptr<WindowContext> g_window;
|
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 auto flags = static_cast<uint32_t>(SDL_GetWindowFlags(g_window->window));
|
||||||
const bool fullscreen =
|
const bool fullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0u;
|
||||||
(flags & static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0u;
|
if (!SDL_SetWindowFullscreen(g_window->window, !fullscreen)) {
|
||||||
const auto mode =
|
|
||||||
fullscreen ? 0u : static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
||||||
if (SDL_SetWindowFullscreen(g_window->window, mode) != 0) {
|
|
||||||
LOGF("Toggle fullscreen failed: %s\n", SDL_GetError());
|
LOGF("Toggle fullscreen failed: %s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -247,7 +212,7 @@ static void GameEventKeyboard(const EventKeyboard& key) {
|
||||||
break;
|
break;
|
||||||
case SDLK_RETURN:
|
case SDLK_RETURN:
|
||||||
case SDLK_KP_ENTER:
|
case SDLK_KP_ENTER:
|
||||||
if (!key.repeat && (key.mod & KMOD_ALT) != 0) {
|
if (!key.repeat && (key.mod & SDL_KMOD_ALT) != 0) {
|
||||||
ToggleDesktopFullscreen();
|
ToggleDesktopFullscreen();
|
||||||
fullscreen_key = key.key_code;
|
fullscreen_key = key.key_code;
|
||||||
}
|
}
|
||||||
|
|
@ -340,15 +305,15 @@ static void GameEventController([[maybe_unused]] const EventController& f) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (f.added) {
|
if (f.added) {
|
||||||
auto* pad = SDL_GameControllerOpen(f.id);
|
auto* pad = SDL_OpenGamepad(f.id);
|
||||||
EXIT_NOT_IMPLEMENTED(pad == nullptr);
|
EXIT_NOT_IMPLEMENTED(pad == nullptr);
|
||||||
int id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(pad));
|
int id = SDL_GetJoystickID(SDL_GetGamepadJoystick(pad));
|
||||||
Controller::Connect(id);
|
Controller::Connect(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f.removed) {
|
if (f.removed) {
|
||||||
Controller::Disconnect(f.id);
|
Controller::Disconnect(f.id);
|
||||||
SDL_GameControllerClose(SDL_GameControllerFromInstanceID(f.id));
|
SDL_CloseGamepad(SDL_GetGamepadFromID(f.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f.down || f.up) {
|
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) {
|
void WindowContext::ProcessWindowEvent(const SDL_WindowEvent& event) {
|
||||||
const auto& window_event = event;
|
const auto& window_event = event;
|
||||||
switch (window_event.event) {
|
switch (window_event.type) {
|
||||||
case SDL_WINDOWEVENT_SHOWN:
|
case SDL_EVENT_WINDOW_SHOWN:
|
||||||
LOGF("Window %" PRIu32 " shown\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " shown\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_HIDDEN:
|
case SDL_EVENT_WINDOW_HIDDEN:
|
||||||
LOGF("Window %" PRIu32 " hidden\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " hidden\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_EXPOSED:
|
case SDL_EVENT_WINDOW_EXPOSED:
|
||||||
LOGF("Window %" PRIu32 " exposed\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " exposed\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_MOVED:
|
case SDL_EVENT_WINDOW_MOVED:
|
||||||
LOGF("Window %" PRIu32 " moved to %" PRId32 ",%" PRId32 "\n", window_event.windowID,
|
LOGF("Window %" PRIu32 " moved to %" PRId32 ",%" PRId32 "\n", window_event.windowID,
|
||||||
window_event.data1, window_event.data2);
|
window_event.data1, window_event.data2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_RESIZED:
|
case SDL_EVENT_WINDOW_RESIZED:
|
||||||
LOGF("Window %" PRIu32 " resized to %" PRId32 "x%" PRId32 "\n", window_event.windowID,
|
LOGF("Window %" PRIu32 " resized to %" PRId32 "x%" PRId32 "\n", window_event.windowID,
|
||||||
window_event.data1, window_event.data2);
|
window_event.data1, window_event.data2);
|
||||||
|
|
||||||
LOGF("m: %d\n", static_cast<int>(SDL_ThreadID()));
|
|
||||||
Resize(window_event.data1, window_event.data2);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||||
LOGF("Window %" PRIu32 " size changed to %" PRId32 "x%" PRId32 "\n",
|
LOGF("Window %" PRIu32 " size changed to %" PRId32 "x%" PRId32 "\n",
|
||||||
window_event.windowID, window_event.data1, window_event.data2);
|
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);
|
Resize(window_event.data1, window_event.data2);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT_MINIMIZED:
|
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||||
LOGF("Window %" PRIu32 " minimized\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " minimized\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
case SDL_EVENT_WINDOW_MAXIMIZED:
|
||||||
LOGF("Window %" PRIu32 " maximized\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " maximized\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_RESTORED:
|
case SDL_EVENT_WINDOW_RESTORED:
|
||||||
LOGF("Window %" PRIu32 " restored\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " restored\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_ENTER:
|
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
||||||
LOGF("Mouse entered window %" PRIu32 "\n", window_event.windowID);
|
LOGF("Mouse entered window %" PRIu32 "\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_LEAVE:
|
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||||
LOGF("Mouse left window %" PRIu32 "\n", window_event.windowID);
|
LOGF("Mouse left window %" PRIu32 "\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||||
LOGF("Window %" PRIu32 " gained keyboard focus\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " gained keyboard focus\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||||
LOGF("Window %" PRIu32 " lost keyboard focus\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " lost keyboard focus\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT_CLOSE:
|
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||||
LOGF("Window %" PRIu32 " closed\n", window_event.windowID);
|
LOGF("Window %" PRIu32 " closed\n", window_event.windowID);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGF("Window %" PRIu32 " got unknown event %" PRIu8 "\n", window_event.windowID,
|
LOGF("Window %" PRIu32 " got unknown event %" PRIu32 "\n", window_event.windowID,
|
||||||
window_event.event);
|
static_cast<uint32_t>(window_event.type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowContext::ProcessDisplayEvent(const SDL_DisplayEvent& display) {
|
void WindowContext::ProcessDisplayEvent(const SDL_DisplayEvent& display) {
|
||||||
bool sdl = false;
|
switch (display.type) {
|
||||||
|
case SDL_EVENT_DISPLAY_ORIENTATION: {
|
||||||
switch (display.event) {
|
LOGF("Display %" PRIu32 " changed orientation to %d - ", display.displayID,
|
||||||
case SDL_DISPLAYEVENT_ORIENTATION: sdl = true; [[fallthrough]];
|
static_cast<int>(display.data1));
|
||||||
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.data1) {
|
switch (display.data1) {
|
||||||
case SDL_ORIENTATION_UNKNOWN: LOGF("UNKNOWN\n"); break;
|
case SDL_ORIENTATION_UNKNOWN: LOGF("UNKNOWN\n"); break;
|
||||||
|
|
@ -488,8 +445,8 @@ void WindowContext::ProcessDisplayEvent(const SDL_DisplayEvent& display) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
LOGF("Display %" PRIu32 " got unknown event 0x%" PRIx8 "\n", display.display,
|
LOGF("Display %" PRIu32 " got unknown event 0x%" PRIx32 "\n", display.displayID,
|
||||||
display.event);
|
static_cast<uint32_t>(display.type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -497,10 +454,9 @@ void WindowContext::ProcessDisplayEvent(const SDL_DisplayEvent& display) {
|
||||||
void WindowContext::ProcessEvent(double time_s) {
|
void WindowContext::ProcessEvent(double time_s) {
|
||||||
auto& game = loop;
|
auto& game = loop;
|
||||||
auto* event = &game.event;
|
auto* event = &game.event;
|
||||||
EXIT_IF(SDL_GetEventState(SDL_DISPLAYEVENT) != SDL_ENABLE);
|
if ((event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP) &&
|
||||||
if ((event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) &&
|
event->key.key == SDLK_F7) {
|
||||||
event->key.keysym.sym == SDLK_F7) {
|
if (event->type == SDL_EVENT_KEY_DOWN && event->key.repeat == 0) {
|
||||||
if (event->type == SDL_KEYDOWN && event->key.repeat == 0) {
|
|
||||||
HostInputToggleMouseToJoystick();
|
HostInputToggleMouseToJoystick();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -508,34 +464,42 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
if (ProcessSystemOverlayInput(*event)) {
|
if (ProcessSystemOverlayInput(*event)) {
|
||||||
return;
|
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) {
|
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_EVENT_KEY_DOWN:
|
||||||
case SDL_KEYUP: {
|
case SDL_EVENT_KEY_UP: {
|
||||||
EventKeyboard key {};
|
EventKeyboard key {};
|
||||||
|
|
||||||
key.down = (event->type == SDL_KEYDOWN);
|
key.down = (event->type == SDL_EVENT_KEY_DOWN);
|
||||||
key.up = (event->type == SDL_KEYUP);
|
key.up = (event->type == SDL_EVENT_KEY_UP);
|
||||||
key.pressed = (event->key.state == SDL_PRESSED);
|
key.pressed = event->key.down;
|
||||||
key.released = (event->key.state == SDL_RELEASED);
|
key.released = !event->key.down;
|
||||||
key.repeat = (event->key.repeat != 0u);
|
key.repeat = (event->key.repeat != 0u);
|
||||||
key.scan_code = event->key.keysym.scancode;
|
key.scan_code = event->key.scancode;
|
||||||
key.key_code = event->key.keysym.sym;
|
key.key_code = event->key.key;
|
||||||
key.mod = event->key.keysym.mod;
|
key.mod = event->key.mod;
|
||||||
key.timestamp_seconds = time_s;
|
key.timestamp_seconds = time_s;
|
||||||
|
|
||||||
GameEventKeyboard(key);
|
GameEventKeyboard(key);
|
||||||
|
|
@ -543,24 +507,20 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_WINDOWEVENT: ProcessWindowEvent(event->window); break;
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||||
|
case SDL_EVENT_MOUSE_BUTTON_UP: {
|
||||||
case SDL_DISPLAYEVENT: ProcessDisplayEvent(event->display); break;
|
|
||||||
|
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
|
||||||
case SDL_MOUSEBUTTONUP: {
|
|
||||||
EventMouse mb {};
|
EventMouse mb {};
|
||||||
|
|
||||||
mb.down = (event->button.type == SDL_MOUSEBUTTONDOWN);
|
mb.down = (event->button.type == SDL_EVENT_MOUSE_BUTTON_DOWN);
|
||||||
mb.up = (event->button.type == SDL_MOUSEBUTTONUP);
|
mb.up = (event->button.type == SDL_EVENT_MOUSE_BUTTON_UP);
|
||||||
mb.left = (event->button.button == SDL_BUTTON_LEFT);
|
mb.left = (event->button.button == SDL_BUTTON_LEFT);
|
||||||
mb.middle = (event->button.button == SDL_BUTTON_MIDDLE);
|
mb.middle = (event->button.button == SDL_BUTTON_MIDDLE);
|
||||||
mb.right = (event->button.button == SDL_BUTTON_RIGHT);
|
mb.right = (event->button.button == SDL_BUTTON_RIGHT);
|
||||||
mb.x1 = (event->button.button == SDL_BUTTON_X1);
|
mb.x1 = (event->button.button == SDL_BUTTON_X1);
|
||||||
mb.x2 = (event->button.button == SDL_BUTTON_X2);
|
mb.x2 = (event->button.button == SDL_BUTTON_X2);
|
||||||
mb.touch = (event->button.which == SDL_TOUCH_MOUSEID);
|
mb.touch = (event->button.which == SDL_TOUCH_MOUSEID);
|
||||||
mb.pressed = (event->button.state == SDL_PRESSED);
|
mb.pressed = event->button.down;
|
||||||
mb.released = (event->button.state == SDL_RELEASED);
|
mb.released = !event->button.down;
|
||||||
mb.num_of_clicks = event->button.clicks;
|
mb.num_of_clicks = event->button.clicks;
|
||||||
mb.wheel = false;
|
mb.wheel = false;
|
||||||
mb.x = event->button.x;
|
mb.x = event->button.x;
|
||||||
|
|
@ -575,7 +535,7 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_MOUSEWHEEL: {
|
case SDL_EVENT_MOUSE_WHEEL: {
|
||||||
EventMouse mb {};
|
EventMouse mb {};
|
||||||
|
|
||||||
mb.down = false;
|
mb.down = false;
|
||||||
|
|
@ -602,7 +562,7 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_MOUSEMOTION: {
|
case SDL_EVENT_MOUSE_MOTION: {
|
||||||
EventMouse mb {};
|
EventMouse mb {};
|
||||||
|
|
||||||
mb.down = false;
|
mb.down = false;
|
||||||
|
|
@ -629,16 +589,16 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_FINGERMOTION:
|
case SDL_EVENT_FINGER_MOTION:
|
||||||
case SDL_FINGERDOWN:
|
case SDL_EVENT_FINGER_DOWN:
|
||||||
case SDL_FINGERUP: {
|
case SDL_EVENT_FINGER_UP: {
|
||||||
EventFinger f {};
|
EventFinger f {};
|
||||||
|
|
||||||
f.down = (event->tfinger.type == SDL_FINGERDOWN);
|
f.down = (event->tfinger.type == SDL_EVENT_FINGER_DOWN);
|
||||||
f.up = (event->tfinger.type == SDL_FINGERUP);
|
f.up = (event->tfinger.type == SDL_EVENT_FINGER_UP);
|
||||||
f.motion = (event->tfinger.type == SDL_FINGERMOTION);
|
f.motion = (event->tfinger.type == SDL_EVENT_FINGER_MOTION);
|
||||||
f.finger_id = static_cast<int>(event->tfinger.fingerId);
|
f.finger_id = static_cast<int>(event->tfinger.fingerID);
|
||||||
f.touch_id = static_cast<int>(event->tfinger.touchId);
|
f.touch_id = static_cast<int>(event->tfinger.touchID);
|
||||||
f.x = event->tfinger.x;
|
f.x = event->tfinger.x;
|
||||||
f.y = event->tfinger.y;
|
f.y = event->tfinger.y;
|
||||||
f.dx = event->tfinger.dx;
|
f.dx = event->tfinger.dx;
|
||||||
|
|
@ -651,13 +611,13 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_CONTROLLERAXISMOTION: {
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
|
||||||
EventController c {};
|
EventController c {};
|
||||||
|
|
||||||
c.id = event->caxis.which;
|
c.id = event->gaxis.which;
|
||||||
c.button = SDL_CONTROLLER_BUTTON_INVALID;
|
c.button = SDL_GAMEPAD_BUTTON_INVALID;
|
||||||
c.axis_id = event->caxis.axis;
|
c.axis_id = event->gaxis.axis;
|
||||||
c.axis_value = event->caxis.value;
|
c.axis_value = event->gaxis.value;
|
||||||
c.down = false;
|
c.down = false;
|
||||||
c.up = false;
|
c.up = false;
|
||||||
c.added = false;
|
c.added = false;
|
||||||
|
|
@ -673,22 +633,22 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||||
case SDL_CONTROLLERBUTTONUP: {
|
case SDL_EVENT_GAMEPAD_BUTTON_UP: {
|
||||||
EventController c {};
|
EventController c {};
|
||||||
|
|
||||||
c.id = event->cbutton.which;
|
c.id = event->gbutton.which;
|
||||||
c.button = event->cbutton.button;
|
c.button = event->gbutton.button;
|
||||||
c.axis_id = SDL_CONTROLLER_AXIS_INVALID;
|
c.axis_id = SDL_GAMEPAD_AXIS_INVALID;
|
||||||
c.axis_value = 0;
|
c.axis_value = 0;
|
||||||
c.down = (event->cbutton.type == SDL_CONTROLLERBUTTONDOWN);
|
c.down = event->gbutton.down;
|
||||||
c.up = (event->cbutton.type == SDL_CONTROLLERBUTTONUP);
|
c.up = !event->gbutton.down;
|
||||||
c.added = false;
|
c.added = false;
|
||||||
c.removed = false;
|
c.removed = false;
|
||||||
c.remapped = false;
|
c.remapped = false;
|
||||||
c.axis = false;
|
c.axis = false;
|
||||||
c.pressed = (event->cbutton.state == SDL_PRESSED);
|
c.pressed = event->gbutton.down;
|
||||||
c.released = (event->cbutton.state == SDL_RELEASED);
|
c.released = !event->gbutton.down;
|
||||||
c.timestamp_seconds = time_s;
|
c.timestamp_seconds = time_s;
|
||||||
|
|
||||||
GameEventController(c);
|
GameEventController(c);
|
||||||
|
|
@ -696,44 +656,44 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_CONTROLLERTOUCHPADDOWN:
|
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
||||||
case SDL_CONTROLLERTOUCHPADMOTION:
|
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
||||||
case SDL_CONTROLLERTOUCHPADUP:
|
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
||||||
if (event->ctouchpad.touchpad == 0) {
|
if (event->gtouchpad.touchpad == 0) {
|
||||||
Controller::SetTouchPad(event->ctouchpad.which, event->ctouchpad.finger,
|
Controller::SetTouchPad(event->gtouchpad.which, event->gtouchpad.finger,
|
||||||
event->ctouchpad.type != SDL_CONTROLLERTOUCHPADUP,
|
event->gtouchpad.type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP,
|
||||||
event->ctouchpad.x, event->ctouchpad.y);
|
event->gtouchpad.x, event->gtouchpad.y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_CONTROLLERSENSORUPDATE: {
|
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: {
|
||||||
const auto& sensor = event->csensor;
|
const auto& sensor = event->gsensor;
|
||||||
if (sensor.sensor == SDL_SENSOR_ACCEL || sensor.sensor == SDL_SENSOR_GYRO) {
|
if (sensor.sensor == SDL_SENSOR_ACCEL || sensor.sensor == SDL_SENSOR_GYRO) {
|
||||||
Controller::SetSensor(sensor.which,
|
Controller::SetSensor(sensor.which,
|
||||||
sensor.sensor == SDL_SENSOR_ACCEL ? Controller::Sensor::Accel
|
sensor.sensor == SDL_SENSOR_ACCEL ? Controller::Sensor::Accel
|
||||||
: Controller::Sensor::Gyro,
|
: Controller::Sensor::Gyro,
|
||||||
sensor.data,
|
sensor.data,
|
||||||
sensor.timestamp_us != 0
|
sensor.sensor_timestamp != 0
|
||||||
? sensor.timestamp_us
|
? sensor.sensor_timestamp / 1000
|
||||||
: static_cast<uint64_t>(sensor.timestamp) * 1000);
|
: sensor.timestamp / 1000);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
case SDL_EVENT_GAMEPAD_ADDED:
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||||
case SDL_CONTROLLERDEVICEREMAPPED: {
|
case SDL_EVENT_GAMEPAD_REMAPPED: {
|
||||||
EventController c {};
|
EventController c {};
|
||||||
|
|
||||||
c.id = event->cdevice.which;
|
c.id = event->gdevice.which;
|
||||||
c.button = SDL_CONTROLLER_BUTTON_INVALID;
|
c.button = SDL_GAMEPAD_BUTTON_INVALID;
|
||||||
c.axis_id = SDL_CONTROLLER_AXIS_INVALID;
|
c.axis_id = SDL_GAMEPAD_AXIS_INVALID;
|
||||||
c.axis_value = 0;
|
c.axis_value = 0;
|
||||||
c.down = false;
|
c.down = false;
|
||||||
c.up = false;
|
c.up = false;
|
||||||
c.added = (event->cdevice.type == SDL_CONTROLLERDEVICEADDED);
|
c.added = (event->gdevice.type == SDL_EVENT_GAMEPAD_ADDED);
|
||||||
c.removed = (event->cdevice.type == SDL_CONTROLLERDEVICEREMOVED);
|
c.removed = (event->gdevice.type == SDL_EVENT_GAMEPAD_REMOVED);
|
||||||
c.remapped = (event->cdevice.type == SDL_CONTROLLERDEVICEREMAPPED);
|
c.remapped = (event->gdevice.type == SDL_EVENT_GAMEPAD_REMAPPED);
|
||||||
c.axis = false;
|
c.axis = false;
|
||||||
c.pressed = false;
|
c.pressed = false;
|
||||||
c.released = 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() {
|
void WindowContext::Run() {
|
||||||
Common::Timer timer;
|
Common::Timer timer;
|
||||||
timer.Start();
|
timer.Start();
|
||||||
|
|
@ -796,7 +715,6 @@ void WindowContext::Run() {
|
||||||
loop.paused.store(false, std::memory_order_release);
|
loop.paused.store(false, std::memory_order_release);
|
||||||
|
|
||||||
while (!loop.need_exit) {
|
while (!loop.need_exit) {
|
||||||
DrainMainThreadTasks();
|
|
||||||
if (loop.paused.load(std::memory_order_acquire)) {
|
if (loop.paused.load(std::memory_order_acquire)) {
|
||||||
if (!timer.IsPaused()) {
|
if (!timer.IsPaused()) {
|
||||||
timer.Pause();
|
timer.Pause();
|
||||||
|
|
@ -820,17 +738,15 @@ static void WindowCreate(WindowContext& context) {
|
||||||
int width = static_cast<int>(context.graphic_ctx.screen_width);
|
int width = static_cast<int>(context.graphic_ctx.screen_width);
|
||||||
int height = static_cast<int>(context.graphic_ctx.screen_height);
|
int height = static_cast<int>(context.graphic_ctx.screen_height);
|
||||||
|
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
// RenderDoc can hide Wayland Vulkan surface support; prefer X11 for captures.
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
if (Config::RenderDocEnabled()) {
|
||||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "0");
|
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11,wayland");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
|
|
||||||
EXIT("%s\n", SDL_GetError());
|
EXIT("%s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
HostInputInit();
|
|
||||||
InitializeSystemOverlayInput();
|
|
||||||
|
|
||||||
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
|
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
|
// 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.
|
// override, and fall back to SDL's normal loader search when no bundle is present.
|
||||||
if (std::getenv("SDL_VULKAN_LIBRARY") == nullptr) {
|
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;
|
const std::string base_path_str = base_path;
|
||||||
SDL_free(base_path);
|
|
||||||
std::string moltenvk_path = base_path_str + "libMoltenVK.dylib";
|
std::string moltenvk_path = base_path_str + "libMoltenVK.dylib";
|
||||||
if (!Common::File::IsFileExisting(moltenvk_path)) {
|
if (!Common::File::IsFileExisting(moltenvk_path)) {
|
||||||
moltenvk_path = base_path_str + "../Frameworks/libMoltenVK.dylib";
|
moltenvk_path = base_path_str + "../Frameworks/libMoltenVK.dylib";
|
||||||
}
|
}
|
||||||
if (Common::File::IsFileExisting(moltenvk_path) &&
|
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());
|
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);
|
window_flags |= static_cast<uint32_t>(SDL_WINDOW_BORDERLESS);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED,
|
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, width, height, window_flags);
|
||||||
SDL_WINDOWPOS_CENTERED, width, height, window_flags);
|
|
||||||
|
|
||||||
if (context.window == nullptr) {
|
if (context.window == nullptr) {
|
||||||
EXIT("%s\n", SDL_GetError());
|
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();
|
context.UpdateIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t WindowContext::InitialWindowFlags(bool fullscreen) noexcept {
|
uint32_t WindowContext::InitialWindowFlags(bool fullscreen) noexcept {
|
||||||
auto flags = static_cast<uint32_t>(SDL_WINDOW_VULKAN);
|
auto flags = static_cast<uint32_t>(SDL_WINDOW_VULKAN);
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
flags |= static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP);
|
flags |= static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN);
|
||||||
}
|
}
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
@ -906,6 +823,7 @@ void WindowRun() {
|
||||||
|
|
||||||
void WindowShutdown() {
|
void WindowShutdown() {
|
||||||
if (g_window != nullptr) {
|
if (g_window != nullptr) {
|
||||||
|
HostInputShutdown();
|
||||||
Controller::EmergencyShutdown();
|
Controller::EmergencyShutdown();
|
||||||
g_window.reset();
|
g_window.reset();
|
||||||
}
|
}
|
||||||
|
|
@ -941,7 +859,7 @@ struct WindowIcon {
|
||||||
void* pixels = nullptr;
|
void* pixels = nullptr;
|
||||||
|
|
||||||
~WindowIcon() {
|
~WindowIcon() {
|
||||||
SDL_FreeSurface(surface);
|
SDL_DestroySurface(surface);
|
||||||
stbi_image_free(pixels);
|
stbi_image_free(pixels);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -965,8 +883,8 @@ static void WindowLoadPngIcon(const std::filesystem::path& path, WindowIcon* ico
|
||||||
|
|
||||||
EXIT_IF(icon->pixels == nullptr);
|
EXIT_IF(icon->pixels == nullptr);
|
||||||
|
|
||||||
icon->surface = SDL_CreateRGBSurfaceWithFormatFrom(icon->pixels, width, height, 32, width * 4,
|
icon->surface = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_RGBA32, icon->pixels,
|
||||||
SDL_PIXELFORMAT_RGBA32);
|
width * 4);
|
||||||
EXIT_NOT_IMPLEMENTED(icon->surface == nullptr);
|
EXIT_NOT_IMPLEMENTED(icon->surface == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1028,7 +946,16 @@ void WindowContext::UpdateTitle() {
|
||||||
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
|
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
|
||||||
device_name, processor_name, frame_num, current_fps);
|
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
|
} // namespace Libs::Graphics
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_WINDOW_WINDOWINTERNAL_H_
|
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_WINDOW_WINDOWINTERNAL_H_
|
||||||
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_WINDOW_WINDOWINTERNAL_H_
|
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_WINDOW_WINDOWINTERNAL_H_
|
||||||
|
|
||||||
#include "SDL_events.h"
|
#include <SDL3/SDL.h>
|
||||||
#include "SDL_video.h"
|
|
||||||
#include "common/threads.h"
|
#include "common/threads.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
@ -48,9 +47,6 @@ struct WindowContext {
|
||||||
void ProcessDisplayEvent(const SDL_DisplayEvent& event);
|
void ProcessDisplayEvent(const SDL_DisplayEvent& event);
|
||||||
void ProcessEvent(double time_seconds);
|
void ProcessEvent(double time_seconds);
|
||||||
void Run();
|
void Run();
|
||||||
// SDL window operations must complete on the main thread.
|
|
||||||
void RunOnMainThread(std::function<void()> task);
|
|
||||||
void DrainMainThreadTasks();
|
|
||||||
|
|
||||||
GraphicContext graphic_ctx;
|
GraphicContext graphic_ctx;
|
||||||
SDL_Window* window = nullptr;
|
SDL_Window* window = nullptr;
|
||||||
|
|
@ -61,12 +57,6 @@ struct WindowContext {
|
||||||
WindowLoopState loop;
|
WindowLoopState loop;
|
||||||
|
|
||||||
Common::Mutex mutex;
|
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
|
} // namespace Libs::Graphics
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ add_executable(launcher WIN32
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
target_link_libraries(launcher Qt6::Concurrent Qt6::Network Qt6::Widgets Vulkan::Headers SDL2-static kyty_git_version)
|
target_link_libraries(launcher Qt6::Concurrent Qt6::Network Qt6::Widgets Vulkan::Headers SDL3::SDL3-static kyty_git_version)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(launcher setupapi)
|
target_link_libraries(launcher setupapi)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mandatoryLineEdit.h"
|
#include "mandatoryLineEdit.h"
|
||||||
#include "SDL.h"
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
|
@ -173,17 +173,18 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||||
microphone->clear();
|
microphone->clear();
|
||||||
microphone->addItem(tr("None"), QString {});
|
microphone->addItem(tr("None"), QString {});
|
||||||
microphone->setToolTip(tr("Microphone used by games. None supplies silence."));
|
microphone->setToolTip(tr("Microphone used by games. None supplies silence."));
|
||||||
SDL_SetMainReady();
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
|
int device_count = 0;
|
||||||
const int device_count = SDL_GetNumAudioDevices(SDL_TRUE);
|
SDL_AudioDeviceID* devices = SDL_GetAudioRecordingDevices(&device_count);
|
||||||
for (int i = 0; i < device_count; i++) {
|
for (int i = 0; i < device_count; i++) {
|
||||||
if (const auto* device = SDL_GetAudioDeviceName(i, SDL_TRUE); device != nullptr) {
|
if (const auto* device = SDL_GetAudioDeviceName(devices[i]); device != nullptr) {
|
||||||
const auto name = QString::fromUtf8(device);
|
const auto name = QString::fromUtf8(device);
|
||||||
if (microphone->findData(name) < 0) {
|
if (microphone->findData(name) < 0) {
|
||||||
microphone->addItem(name, name);
|
microphone->addItem(name, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SDL_free(devices);
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
} else {
|
} else {
|
||||||
microphone->setToolTip(tr("Microphones could not be listed: %1")
|
microphone->setToolTip(tr("Microphones could not be listed: %1")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "libs/audio.h"
|
#include "libs/audio.h"
|
||||||
|
|
||||||
#include "SDL.h"
|
#include <SDL3/SDL.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
|
|
@ -115,8 +115,7 @@ private:
|
||||||
int channels_num = 0;
|
int channels_num = 0;
|
||||||
int volume[12] = {};
|
int volume[12] = {};
|
||||||
|
|
||||||
SDL_AudioDeviceID audio_device = 0;
|
SDL_AudioStream* stream = nullptr;
|
||||||
SDL_AudioSpec audio_spec = {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PortIn {
|
struct PortIn {
|
||||||
|
|
@ -127,7 +126,8 @@ private:
|
||||||
uint32_t freq = 0;
|
uint32_t freq = 0;
|
||||||
uint32_t bytes_per_frame = 0;
|
uint32_t bytes_per_frame = 0;
|
||||||
uint64_t last_input_time = 0;
|
uint64_t last_input_time = 0;
|
||||||
SDL_AudioDeviceID audio_device = 0;
|
SDL_AudioStream* stream = nullptr;
|
||||||
|
SDL_AudioDeviceID device = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
PortIn* GetAudioInPort(Id handle); // Caller holds m_mutex.
|
PortIn* GetAudioInPort(Id handle); // Caller holds m_mutex.
|
||||||
|
|
@ -242,13 +242,13 @@ uint32_t Audio::OutputChannels(const PortOut& port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_AudioFormat Audio::SdlFormat(Format format) {
|
SDL_AudioFormat Audio::SdlFormat(Format format) {
|
||||||
return FormatIsFloat(format) ? AUDIO_F32SYS : AUDIO_S16SYS;
|
return FormatIsFloat(format) ? SDL_AUDIO_F32 : SDL_AUDIO_S16;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Audio::OpenSdlDevice(PortOut* port) {
|
bool Audio::OpenSdlDevice(PortOut* port) {
|
||||||
EXIT_IF(port == nullptr);
|
EXIT_IF(port == nullptr);
|
||||||
|
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
LOGF("AudioOut: SDL audio init failed: %s\n", SDL_GetError());
|
LOGF("AudioOut: SDL audio init failed: %s\n", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -256,37 +256,34 @@ bool Audio::OpenSdlDevice(PortOut* port) {
|
||||||
SDL_AudioSpec desired {};
|
SDL_AudioSpec desired {};
|
||||||
desired.freq = static_cast<int>(port->freq);
|
desired.freq = static_cast<int>(port->freq);
|
||||||
desired.format = SdlFormat(port->format);
|
desired.format = SdlFormat(port->format);
|
||||||
desired.channels = static_cast<Uint8>(OutputChannels(*port));
|
desired.channels = static_cast<int>(OutputChannels(*port));
|
||||||
desired.samples = static_cast<Uint16>(port->samples_num);
|
|
||||||
desired.callback = nullptr;
|
|
||||||
|
|
||||||
SDL_AudioSpec obtained {};
|
port->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &desired, nullptr,
|
||||||
|
nullptr);
|
||||||
port->audio_device =
|
if (port->stream == nullptr) {
|
||||||
SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
LOGF("AudioOut: SDL_OpenAudioDeviceStream failed: %s\n", SDL_GetError());
|
||||||
if (port->audio_device == 0) {
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
LOGF("AudioOut: SDL_OpenAudioDevice failed: %s\n", SDL_GetError());
|
return false;
|
||||||
|
}
|
||||||
|
if (!SDL_ResumeAudioStreamDevice(port->stream)) {
|
||||||
|
LOGF("AudioOut: SDL_ResumeAudioStreamDevice failed: %s\n", SDL_GetError());
|
||||||
|
CloseSdlDevice(port);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
port->audio_spec = obtained;
|
LOGF("AudioOut: opened SDL stream (%d Hz, %d ch, format 0x%04x)\n", desired.freq,
|
||||||
SDL_PauseAudioDevice(port->audio_device, 0);
|
desired.channels, static_cast<unsigned>(desired.format));
|
||||||
|
|
||||||
LOGF("AudioOut: opened SDL device (%d Hz, %u ch, format 0x%04x)\n", obtained.freq,
|
|
||||||
obtained.channels, obtained.format);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::CloseSdlDevice(PortOut* port) {
|
void Audio::CloseSdlDevice(PortOut* port) {
|
||||||
EXIT_IF(port == nullptr);
|
EXIT_IF(port == nullptr);
|
||||||
|
|
||||||
if (port->audio_device != 0 && SDL_WasInit(SDL_INIT_AUDIO) != 0) {
|
if (port->stream != nullptr) {
|
||||||
SDL_ClearQueuedAudio(port->audio_device);
|
SDL_DestroyAudioStream(port->stream);
|
||||||
SDL_CloseAudioDevice(port->audio_device);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
}
|
}
|
||||||
|
port->stream = nullptr;
|
||||||
port->audio_device = 0;
|
|
||||||
port->audio_spec = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* Audio::PrepareOutputBuffer(const PortOut& port, const void* data,
|
const void* Audio::PrepareOutputBuffer(const PortOut& port, const void* data,
|
||||||
|
|
@ -364,7 +361,7 @@ const void* Audio::PrepareOutputBuffer(const PortOut& port, const void* data,
|
||||||
bool Audio::QueueSdlAudio(PortOut* port, const void* data, bool blocking) {
|
bool Audio::QueueSdlAudio(PortOut* port, const void* data, bool blocking) {
|
||||||
EXIT_IF(port == nullptr);
|
EXIT_IF(port == nullptr);
|
||||||
|
|
||||||
if (port->audio_device == 0 || data == nullptr) {
|
if (port->stream == nullptr || data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -374,56 +371,25 @@ bool Audio::QueueSdlAudio(PortOut* port, const void* data, bool blocking) {
|
||||||
const auto prepared_size =
|
const auto prepared_size =
|
||||||
BytesPerSample(port->format) * output_channels * port->samples_num;
|
BytesPerSample(port->format) * output_channels * port->samples_num;
|
||||||
|
|
||||||
std::vector<uint8_t> convert_buffer;
|
|
||||||
const void* queue_data = prepared_data;
|
|
||||||
uint32_t queue_size = prepared_size;
|
|
||||||
|
|
||||||
SDL_AudioCVT cvt {};
|
|
||||||
const int cvt_result =
|
|
||||||
SDL_BuildAudioCVT(&cvt, SdlFormat(port->format), static_cast<Uint8>(output_channels),
|
|
||||||
static_cast<int>(port->freq), port->audio_spec.format,
|
|
||||||
port->audio_spec.channels, port->audio_spec.freq);
|
|
||||||
|
|
||||||
if (cvt_result < 0) {
|
|
||||||
LOGF("AudioOut: SDL_BuildAudioCVT failed: %s\n", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cvt_result > 0) {
|
|
||||||
convert_buffer.resize(prepared_size * cvt.len_mult);
|
|
||||||
std::memcpy(convert_buffer.data(), prepared_data, prepared_size);
|
|
||||||
|
|
||||||
cvt.buf = convert_buffer.data();
|
|
||||||
cvt.len = static_cast<int>(prepared_size);
|
|
||||||
|
|
||||||
if (SDL_ConvertAudio(&cvt) < 0) {
|
|
||||||
LOGF("AudioOut: SDL_ConvertAudio failed: %s\n", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
queue_data = cvt.buf;
|
|
||||||
queue_size = static_cast<uint32_t>(cvt.len_cvt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blocking) {
|
if (blocking) {
|
||||||
constexpr uint64_t target_latency_us = 40000;
|
constexpr uint64_t target_latency_us = 40000;
|
||||||
const auto buffer_us = port->freq != 0 ? (1000000ULL * port->samples_num) / port->freq : 0;
|
const auto buffer_us = port->freq != 0 ? (1000000ULL * port->samples_num) / port->freq : 0;
|
||||||
const auto buffers =
|
const auto buffers =
|
||||||
buffer_us != 0 ? static_cast<uint32_t>((target_latency_us + buffer_us - 1) / buffer_us)
|
buffer_us != 0 ? static_cast<uint32_t>((target_latency_us + buffer_us - 1) / buffer_us)
|
||||||
: 2u;
|
: 2u;
|
||||||
const auto min_queued_size = queue_size * std::clamp(buffers, 2u, 16u);
|
const auto min_queued_size = prepared_size * std::clamp(buffers, 2u, 16u);
|
||||||
const auto wait_start = LibKernel::KernelGetProcessTime();
|
const auto wait_start = LibKernel::KernelGetProcessTime();
|
||||||
while (SDL_GetQueuedAudioSize(port->audio_device) > min_queued_size) {
|
while (SDL_GetAudioStreamQueued(port->stream) > static_cast<int>(min_queued_size)) {
|
||||||
if (LibKernel::KernelGetProcessTime() - wait_start > 200000) {
|
if (LibKernel::KernelGetProcessTime() - wait_start > 200000) {
|
||||||
SDL_ClearQueuedAudio(port->audio_device);
|
SDL_ClearAudioStream(port->stream);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Common::Thread::SleepMicro(1000);
|
Common::Thread::SleepMicro(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_QueueAudio(port->audio_device, queue_data, queue_size) < 0) {
|
if (!SDL_PutAudioStreamData(port->stream, prepared_data, static_cast<int>(prepared_size))) {
|
||||||
LOGF("AudioOut: SDL_QueueAudio failed: %s\n", SDL_GetError());
|
LOGF("AudioOut: SDL_PutAudioStreamData failed: %s\n", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -498,7 +464,7 @@ bool Audio::AudioOutHasDevice(Id handle) {
|
||||||
Common::LockGuard lock(m_mutex);
|
Common::LockGuard lock(m_mutex);
|
||||||
|
|
||||||
return (handle.GetId() >= 0 && handle.GetId() < OUT_PORTS_MAX &&
|
return (handle.GetId() >= 0 && handle.GetId() < OUT_PORTS_MAX &&
|
||||||
m_out_ports[handle.GetId()].used && m_out_ports[handle.GetId()].audio_device != 0);
|
m_out_ports[handle.GetId()].used && m_out_ports[handle.GetId()].stream != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Audio::AudioOutGetStatus(Id handle, int* type, int* channels_num) {
|
bool Audio::AudioOutGetStatus(Id handle, int* type, int* channels_num) {
|
||||||
|
|
@ -557,7 +523,7 @@ uint32_t Audio::AudioOutOutputs(OutputParam* params, uint32_t num, bool blocking
|
||||||
|
|
||||||
bool any_port_has_device = false;
|
bool any_port_has_device = false;
|
||||||
for (uint32_t i = 0; i < num; i++) {
|
for (uint32_t i = 0; i < num; i++) {
|
||||||
if (m_out_ports[params[i].handle.GetId()].audio_device != 0) {
|
if (m_out_ports[params[i].handle.GetId()].stream != nullptr) {
|
||||||
any_port_has_device = true;
|
any_port_has_device = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -583,35 +549,65 @@ uint32_t Audio::AudioOutOutputs(OutputParam* params, uint32_t num, bool blocking
|
||||||
return first_port.samples_num;
|
return first_port.samples_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool RecordingDevicePresent(SDL_AudioDeviceID device) {
|
||||||
|
int count = 0;
|
||||||
|
SDL_AudioDeviceID* devices = SDL_GetAudioRecordingDevices(&count);
|
||||||
|
bool present = false;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (devices[i] == device) {
|
||||||
|
present = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_free(devices);
|
||||||
|
return present;
|
||||||
|
}
|
||||||
|
|
||||||
void Audio::OpenSdlDevice(PortIn* port, Format format) {
|
void Audio::OpenSdlDevice(PortIn* port, Format format) {
|
||||||
const auto& name = Config::GetAudioInputDevice();
|
const auto& name = Config::GetAudioInputDevice();
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
LOGF("AudioIn: SDL init failed: %s\n", SDL_GetError());
|
LOGF("AudioIn: SDL init failed: %s\n", SDL_GetError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int device_count = 0;
|
||||||
|
SDL_AudioDeviceID* devices = SDL_GetAudioRecordingDevices(&device_count);
|
||||||
|
SDL_AudioDeviceID device = 0;
|
||||||
|
for (int i = 0; i < device_count; i++) {
|
||||||
|
const char* device_name = SDL_GetAudioDeviceName(devices[i]);
|
||||||
|
if (device_name != nullptr && name == device_name) {
|
||||||
|
device = devices[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_free(devices);
|
||||||
|
if (device == 0) {
|
||||||
|
LOGF("AudioIn: cannot find '%s'; using silence\n", name.c_str());
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
SDL_AudioSpec desired {};
|
SDL_AudioSpec desired {};
|
||||||
desired.freq = static_cast<int>(port->freq);
|
desired.freq = static_cast<int>(port->freq);
|
||||||
desired.format = SdlFormat(format);
|
desired.format = SdlFormat(format);
|
||||||
desired.channels = static_cast<Uint8>(port->bytes_per_frame / BytesPerSample(format));
|
desired.channels = static_cast<int>(port->bytes_per_frame / BytesPerSample(format));
|
||||||
desired.samples = static_cast<Uint16>(port->samples_num);
|
port->stream = SDL_OpenAudioDeviceStream(device, &desired, nullptr, nullptr);
|
||||||
// SDL converts capture data to the guest format when allowed_changes is zero.
|
if (port->stream == nullptr) {
|
||||||
port->audio_device = SDL_OpenAudioDevice(name.c_str(), 1, &desired, nullptr, 0);
|
|
||||||
if (port->audio_device == 0) {
|
|
||||||
LOGF("AudioIn: cannot open '%s': %s; using silence\n", name.c_str(), SDL_GetError());
|
LOGF("AudioIn: cannot open '%s': %s; using silence\n", name.c_str(), SDL_GetError());
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOGF("AudioIn: opened '%s' (%u Hz, %u ch)\n", name.c_str(), port->freq, desired.channels);
|
port->device = device;
|
||||||
|
LOGF("AudioIn: opened '%s' (%u Hz, %d ch)\n", name.c_str(), port->freq, desired.channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::CloseSdlDevice(PortIn* port) {
|
void Audio::CloseSdlDevice(PortIn* port) {
|
||||||
if (port->audio_device != 0) {
|
if (port->stream != nullptr) {
|
||||||
SDL_CloseAudioDevice(port->audio_device);
|
SDL_DestroyAudioStream(port->stream);
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
port->audio_device = 0;
|
port->stream = nullptr;
|
||||||
|
port->device = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -668,8 +664,7 @@ int Audio::AudioInGetSilentState(Id handle) {
|
||||||
if (port == nullptr) {
|
if (port == nullptr) {
|
||||||
return AUDIO_IN_ERROR_INVALID_HANDLE;
|
return AUDIO_IN_ERROR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
if (port->audio_device == 0 ||
|
if (port->stream == nullptr || !RecordingDevicePresent(port->device)) {
|
||||||
SDL_GetAudioDeviceStatus(port->audio_device) == SDL_AUDIO_STOPPED) {
|
|
||||||
return AUDIO_IN_SILENT_STATE_DEVICE_NONE;
|
return AUDIO_IN_SILENT_STATE_DEVICE_NONE;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -689,9 +684,9 @@ int Audio::AudioInInput(Id handle, void* dest) {
|
||||||
port->busy = true;
|
port->busy = true;
|
||||||
snapshot = *port;
|
snapshot = *port;
|
||||||
}
|
}
|
||||||
if (snapshot.audio_device != 0 && dest != nullptr) {
|
bool failed = snapshot.stream != nullptr && dest != nullptr &&
|
||||||
SDL_PauseAudioDevice(snapshot.audio_device, 0);
|
(!RecordingDevicePresent(snapshot.device) ||
|
||||||
}
|
!SDL_ResumeAudioStreamDevice(snapshot.stream));
|
||||||
|
|
||||||
const uint64_t block_time = (1000000ULL * snapshot.samples_num) / snapshot.freq;
|
const uint64_t block_time = (1000000ULL * snapshot.samples_num) / snapshot.freq;
|
||||||
uint64_t wait_time = 0;
|
uint64_t wait_time = 0;
|
||||||
|
|
@ -699,7 +694,7 @@ int Audio::AudioInInput(Id handle, void* dest) {
|
||||||
if (dest != nullptr) {
|
if (dest != nullptr) {
|
||||||
wait_time = block_time;
|
wait_time = block_time;
|
||||||
}
|
}
|
||||||
} else if (snapshot.last_input_time != 0 && (snapshot.audio_device == 0 || dest == nullptr)) {
|
} else if (snapshot.last_input_time != 0 && (snapshot.stream == nullptr || dest == nullptr)) {
|
||||||
const auto now = LibKernel::KernelGetProcessTime();
|
const auto now = LibKernel::KernelGetProcessTime();
|
||||||
const auto next = snapshot.last_input_time + block_time;
|
const auto next = snapshot.last_input_time + block_time;
|
||||||
if (next > now) {
|
if (next > now) {
|
||||||
|
|
@ -708,31 +703,41 @@ int Audio::AudioInInput(Id handle, void* dest) {
|
||||||
}
|
}
|
||||||
Common::Thread::SleepMicro(wait_time);
|
Common::Thread::SleepMicro(wait_time);
|
||||||
uint32_t frames = snapshot.samples_num;
|
uint32_t frames = snapshot.samples_num;
|
||||||
bool failed = false;
|
bool timed_out = false;
|
||||||
if (dest != nullptr) {
|
if (dest != nullptr) {
|
||||||
if (snapshot.audio_device != 0) {
|
if (snapshot.stream != nullptr && !failed) {
|
||||||
while (!snapshot.asynchronous &&
|
const int requested = static_cast<int>(frames * snapshot.bytes_per_frame);
|
||||||
SDL_GetQueuedAudioSize(snapshot.audio_device) <
|
const auto deadline = LibKernel::KernelGetProcessTime() +
|
||||||
frames * snapshot.bytes_per_frame &&
|
std::max<uint64_t>(20000, block_time * 4);
|
||||||
SDL_GetAudioDeviceStatus(snapshot.audio_device) == SDL_AUDIO_PLAYING) {
|
const auto device = SDL_GetAudioStreamDevice(snapshot.stream);
|
||||||
|
int available = SDL_GetAudioStreamAvailable(snapshot.stream);
|
||||||
|
while (!snapshot.asynchronous && available >= 0 &&
|
||||||
|
available < requested &&
|
||||||
|
device != 0 && !SDL_AudioDevicePaused(device) &&
|
||||||
|
LibKernel::KernelGetProcessTime() < deadline) {
|
||||||
Common::Thread::SleepMicro(1000);
|
Common::Thread::SleepMicro(1000);
|
||||||
|
available = SDL_GetAudioStreamAvailable(snapshot.stream);
|
||||||
}
|
}
|
||||||
failed = SDL_GetAudioDeviceStatus(snapshot.audio_device) != SDL_AUDIO_PLAYING;
|
failed = available < 0 || device == 0 || SDL_AudioDevicePaused(device);
|
||||||
if (!failed) {
|
timed_out = !snapshot.asynchronous && available < requested;
|
||||||
|
if (!failed && !timed_out) {
|
||||||
if (snapshot.asynchronous) {
|
if (snapshot.asynchronous) {
|
||||||
frames = AUDIO_IN_GRAIN_MAX_ASYNC;
|
frames = AUDIO_IN_GRAIN_MAX_ASYNC;
|
||||||
}
|
}
|
||||||
frames = SDL_DequeueAudio(snapshot.audio_device, dest,
|
const int bytes = SDL_GetAudioStreamData(
|
||||||
frames * snapshot.bytes_per_frame) /
|
snapshot.stream, dest, static_cast<int>(frames * snapshot.bytes_per_frame));
|
||||||
snapshot.bytes_per_frame;
|
failed = bytes < 0;
|
||||||
|
if (!failed) {
|
||||||
|
frames = static_cast<uint32_t>(bytes) / snapshot.bytes_per_frame;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (snapshot.audio_device == 0 || failed) {
|
if (snapshot.stream == nullptr || failed || timed_out) {
|
||||||
std::memset(dest, 0, frames * snapshot.bytes_per_frame);
|
std::memset(dest, 0, frames * snapshot.bytes_per_frame);
|
||||||
}
|
}
|
||||||
} else if (snapshot.audio_device != 0) {
|
} else if (snapshot.stream != nullptr) {
|
||||||
SDL_PauseAudioDevice(snapshot.audio_device, 1);
|
SDL_PauseAudioStreamDevice(snapshot.stream);
|
||||||
SDL_ClearQueuedAudio(snapshot.audio_device);
|
SDL_ClearAudioStream(snapshot.stream);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Common::LockGuard lock(m_mutex);
|
Common::LockGuard lock(m_mutex);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "libs/controller.h"
|
#include "libs/controller.h"
|
||||||
|
|
||||||
#include "SDL.h"
|
#include <SDL3/SDL.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
|
|
@ -293,11 +293,11 @@ void GameController::Connect(int id) {
|
||||||
m_connected_ids.push_back(id);
|
m_connected_ids.push_back(id);
|
||||||
|
|
||||||
if (id != HOST_INPUT_CONTROLLER_ID) {
|
if (id != HOST_INPUT_CONTROLLER_ID) {
|
||||||
if (auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(id));
|
if (auto* pad = SDL_GetGamepadFromID(static_cast<SDL_JoystickID>(id));
|
||||||
pad != nullptr) {
|
pad != nullptr) {
|
||||||
for (auto sensor: {SDL_SENSOR_ACCEL, SDL_SENSOR_GYRO}) {
|
for (auto sensor: {SDL_SENSOR_ACCEL, SDL_SENSOR_GYRO}) {
|
||||||
if (SDL_GameControllerHasSensor(pad, sensor) &&
|
if (SDL_GamepadHasSensor(pad, sensor) &&
|
||||||
SDL_GameControllerSetSensorEnabled(pad, sensor, SDL_TRUE) != 0) {
|
!SDL_SetGamepadSensorEnabled(pad, sensor, true)) {
|
||||||
LOGF("\t enabling controller sensor failed: %s\n", SDL_GetError());
|
LOGF("\t enabling controller sensor failed: %s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -519,22 +519,22 @@ void GameController::ResetInputState() {
|
||||||
void GameController::ReleaseHostPads() {
|
void GameController::ReleaseHostPads() {
|
||||||
Common::LockGuard lock(m_mutex);
|
Common::LockGuard lock(m_mutex);
|
||||||
|
|
||||||
std::vector<SDL_GameController*> pads;
|
std::vector<SDL_Gamepad*> pads;
|
||||||
for (const auto id: m_connected_ids) {
|
for (const auto id: m_connected_ids) {
|
||||||
if (id == HOST_INPUT_CONTROLLER_ID) {
|
if (id == HOST_INPUT_CONTROLLER_ID) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(id));
|
if (auto* pad = SDL_GetGamepadFromID(static_cast<SDL_JoystickID>(id));
|
||||||
pad != nullptr) {
|
pad != nullptr) {
|
||||||
if (SDL_GameControllerGetType(pad) == SDL_CONTROLLER_TYPE_PS5) {
|
if (SDL_GetGamepadType(pad) == SDL_GAMEPAD_TYPE_PS5) {
|
||||||
DualSenseEffects effect {};
|
DualSenseEffects effect {};
|
||||||
effect.enable_bits = 0x0c;
|
effect.enable_bits = 0x0c;
|
||||||
effect.right_trigger[0] = 0x05;
|
effect.right_trigger[0] = 0x05;
|
||||||
effect.left_trigger[0] = 0x05;
|
effect.left_trigger[0] = 0x05;
|
||||||
(void)SDL_GameControllerSendEffect(pad, &effect, sizeof(effect));
|
(void)SDL_SendGamepadEffect(pad, &effect, sizeof(effect));
|
||||||
}
|
}
|
||||||
(void)SDL_GameControllerRumble(pad, 0, 0, 0);
|
(void)SDL_RumbleGamepad(pad, 0, 0, 0);
|
||||||
(void)SDL_GameControllerSetLED(pad, 0, 0, 0);
|
(void)SDL_SetGamepadLED(pad, 0, 0, 0);
|
||||||
pads.push_back(pad);
|
pads.push_back(pad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -542,7 +542,7 @@ void GameController::ReleaseHostPads() {
|
||||||
if (!pads.empty()) {
|
if (!pads.empty()) {
|
||||||
SDL_Delay(RELEASE_FLUSH_MS);
|
SDL_Delay(RELEASE_FLUSH_MS);
|
||||||
for (auto* pad: pads) {
|
for (auto* pad: pads) {
|
||||||
SDL_GameControllerClose(pad);
|
SDL_CloseGamepad(pad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -556,23 +556,23 @@ void GameController::SetVibration(uint8_t large_motor, uint8_t small_motor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(m_active_id));
|
auto* pad = SDL_GetGamepadFromID(static_cast<SDL_JoystickID>(m_active_id));
|
||||||
if (pad == nullptr) {
|
if (pad == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto large = static_cast<uint16_t>(large_motor * 0x101U);
|
const auto large = static_cast<uint16_t>(large_motor * 0x101U);
|
||||||
const auto small = static_cast<uint16_t>(small_motor * 0x101U);
|
const auto small = static_cast<uint16_t>(small_motor * 0x101U);
|
||||||
if (SDL_GameControllerRumble(pad, large, small, RUMBLE_DURATION_MS) != 0) {
|
if (!SDL_RumbleGamepad(pad, large, small, RUMBLE_DURATION_MS)) {
|
||||||
LOGF("\t rumble failed: %s\n", SDL_GetError());
|
LOGF("\t rumble failed: %s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::SetLightBar(uint8_t r, uint8_t g, uint8_t b) {
|
void GameController::SetLightBar(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
Common::LockGuard lock(m_mutex);
|
Common::LockGuard lock(m_mutex);
|
||||||
if (auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(m_active_id));
|
if (auto* pad = SDL_GetGamepadFromID(static_cast<SDL_JoystickID>(m_active_id));
|
||||||
pad != nullptr) {
|
pad != nullptr) {
|
||||||
(void)SDL_GameControllerSetLED(pad, r, g, b);
|
(void)SDL_SetGamepadLED(pad, r, g, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -599,9 +599,9 @@ bool GameController::SetTriggerEffect(const PadTriggerEffectParam& param) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::LockGuard lock(m_mutex);
|
Common::LockGuard lock(m_mutex);
|
||||||
auto* pad = SDL_GameControllerFromInstanceID(static_cast<SDL_JoystickID>(m_active_id));
|
auto* pad = SDL_GetGamepadFromID(static_cast<SDL_JoystickID>(m_active_id));
|
||||||
if (pad != nullptr && SDL_GameControllerGetType(pad) == SDL_CONTROLLER_TYPE_PS5) {
|
if (pad != nullptr && SDL_GetGamepadType(pad) == SDL_GAMEPAD_TYPE_PS5) {
|
||||||
(void)SDL_GameControllerSendEffect(pad, &effect, sizeof(effect));
|
(void)SDL_SendGamepadEffect(pad, &effect, sizeof(effect));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include "libs/libs.h"
|
#include "libs/libs.h"
|
||||||
#include "loader/symbolDatabase.h"
|
#include "loader/symbolDatabase.h"
|
||||||
|
|
||||||
#include <SDL_stdinc.h>
|
#include <SDL3/SDL_stdinc.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#include "SDL.h"
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_main.h>
|
||||||
|
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
#include "common/file.h"
|
#include "common/file.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
|
@ -478,11 +480,11 @@ int main() {
|
||||||
Config::Load(options);
|
Config::Load(options);
|
||||||
subsystems.Initialize<Log::Lifecycle>();
|
subsystems.Initialize<Log::Lifecycle>();
|
||||||
|
|
||||||
Check(SDL_InitSubSystem(SDL_INIT_VIDEO) == 0, "initialize Vulkan test video");
|
Check(SDL_InitSubSystem(SDL_INIT_VIDEO), "initialize Vulkan test video");
|
||||||
auto graphics = std::make_unique<Libs::Graphics::WindowContext>();
|
auto graphics = std::make_unique<Libs::Graphics::WindowContext>();
|
||||||
graphics->graphic_ctx.screen_width = 64;
|
graphics->graphic_ctx.screen_width = 64;
|
||||||
graphics->graphic_ctx.screen_height = 64;
|
graphics->graphic_ctx.screen_height = 64;
|
||||||
graphics->window = SDL_CreateWindow("KernelFileSystemTests", 0, 0, 64, 64,
|
graphics->window = SDL_CreateWindow("KernelFileSystemTests", 64, 64,
|
||||||
SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN);
|
SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN);
|
||||||
Check(graphics->window != nullptr, "create hidden Vulkan test window");
|
Check(graphics->window != nullptr, "create hidden Vulkan test window");
|
||||||
graphics->CreateVulkan();
|
graphics->CreateVulkan();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue