mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
mac: Handle SDL init and events on main thread
This commit is contained in:
parent
f0b44e7dca
commit
4f2d77e12e
@ -9,6 +9,7 @@
|
|||||||
#include "config/LaunchSettings.h"
|
#include "config/LaunchSettings.h"
|
||||||
#include "wxgui/GettingStartedDialog.h"
|
#include "wxgui/GettingStartedDialog.h"
|
||||||
#include "input/InputManager.h"
|
#include "input/InputManager.h"
|
||||||
|
#include "input/api/SDL/SDLControllerProvider.h"
|
||||||
#include "wxgui/helpers/wxHelpers.h"
|
#include "wxgui/helpers/wxHelpers.h"
|
||||||
#include "Cemu/ncrypto/ncrypto.h"
|
#include "Cemu/ncrypto/ncrypto.h"
|
||||||
#include "wxgui/input/HotkeySettings.h"
|
#include "wxgui/input/HotkeySettings.h"
|
||||||
@ -26,6 +27,7 @@
|
|||||||
#include <wx/filename.h>
|
#include <wx/filename.h>
|
||||||
#include <wx/stdpaths.h>
|
#include <wx/stdpaths.h>
|
||||||
#include <wx/clipbrd.h>
|
#include <wx/clipbrd.h>
|
||||||
|
#include <wx/timer.h>
|
||||||
#include "wxHelper.h"
|
#include "wxHelper.h"
|
||||||
|
|
||||||
#include "Cafe/TitleList/TitleList.h"
|
#include "Cafe/TitleList/TitleList.h"
|
||||||
@ -332,8 +334,18 @@ bool CemuApp::OnInit()
|
|||||||
#ifdef CEMU_DEBUG_ASSERT
|
#ifdef CEMU_DEBUG_ASSERT
|
||||||
UnitTests();
|
UnitTests();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
SDLControllerProvider::InitSDL();
|
||||||
|
#endif
|
||||||
CemuCommonInit();
|
CemuCommonInit();
|
||||||
|
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
m_sdlEventPumpTimer = new wxTimer(this);
|
||||||
|
Bind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this);
|
||||||
|
m_sdlEventPumpTimer->Start(5, wxTIMER_CONTINUOUS);
|
||||||
|
#endif
|
||||||
|
|
||||||
wxInitAllImageHandlers();
|
wxInitAllImageHandlers();
|
||||||
|
|
||||||
// fill colour db
|
// fill colour db
|
||||||
@ -389,9 +401,22 @@ bool CemuApp::OnInit()
|
|||||||
|
|
||||||
int CemuApp::OnExit()
|
int CemuApp::OnExit()
|
||||||
{
|
{
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
if (m_sdlEventPumpTimer)
|
||||||
|
{
|
||||||
|
m_sdlEventPumpTimer->Stop();
|
||||||
|
Unbind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this);
|
||||||
|
delete m_sdlEventPumpTimer;
|
||||||
|
m_sdlEventPumpTimer = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
wxApp::OnExit();
|
wxApp::OnExit();
|
||||||
wxTheClipboard->Flush();
|
wxTheClipboard->Flush();
|
||||||
InputManager::instance().Shutdown();
|
InputManager::instance().Shutdown();
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
SDLControllerProvider::ShutdownSDL();
|
||||||
|
#endif
|
||||||
#if BOOST_OS_WINDOWS
|
#if BOOST_OS_WINDOWS
|
||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
#else
|
#else
|
||||||
@ -399,6 +424,15 @@ int CemuApp::OnExit()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
void CemuApp::OnSDLEventPumpTimer(wxTimerEvent& event)
|
||||||
|
{
|
||||||
|
// this callback is only used on macOS where SDL event functions need to be called on the main thread
|
||||||
|
// on other platforms SDLControllerProvider creates a separate thread for SDL event polling
|
||||||
|
SDLControllerProvider::PumpSDLEvents();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if BOOST_OS_WINDOWS
|
#if BOOST_OS_WINDOWS
|
||||||
void DumpThreadStackTrace();
|
void DumpThreadStackTrace();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
class wxTimer;
|
||||||
|
class wxTimerEvent;
|
||||||
|
|
||||||
class CemuApp : public wxApp
|
class CemuApp : public wxApp
|
||||||
{
|
{
|
||||||
@ -30,6 +32,10 @@ private:
|
|||||||
static std::vector<const wxLanguageInfo*> GetAvailableTranslationLanguages(wxTranslations* translationsMgr);
|
static std::vector<const wxLanguageInfo*> GetAvailableTranslationLanguages(wxTranslations* translationsMgr);
|
||||||
|
|
||||||
MainWindow* m_mainFrame = nullptr;
|
MainWindow* m_mainFrame = nullptr;
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
void OnSDLEventPumpTimer(wxTimerEvent& event);
|
||||||
|
wxTimer* m_sdlEventPumpTimer = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
wxLocale m_locale;
|
wxLocale m_locale;
|
||||||
std::vector<const wxLanguageInfo*> m_availableTranslations;
|
std::vector<const wxLanguageInfo*> m_availableTranslations;
|
||||||
|
|||||||
@ -16,28 +16,43 @@ struct SDL_JoystickGUIDHash
|
|||||||
|
|
||||||
SDLControllerProvider::SDLControllerProvider()
|
SDLControllerProvider::SDLControllerProvider()
|
||||||
{
|
{
|
||||||
m_motion_states.reserve(8);
|
#if !BOOST_OS_MACOS
|
||||||
|
std::scoped_lock _l(s_mutex);
|
||||||
m_running = true;
|
if (s_initCount.fetch_add(1) == 0)
|
||||||
m_thread = std::thread(&SDLControllerProvider::event_thread, this);
|
{
|
||||||
|
s_running = true;
|
||||||
|
s_thread = std::thread(&SDLControllerProvider::event_thread, this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SDLControllerProvider::~SDLControllerProvider()
|
SDLControllerProvider::~SDLControllerProvider()
|
||||||
{
|
{
|
||||||
if (m_running)
|
#if !BOOST_OS_MACOS
|
||||||
|
bool shutdownSDL = false;
|
||||||
|
{
|
||||||
|
std::scoped_lock _l(s_mutex);
|
||||||
|
if (s_initCount.fetch_sub(1) == 1)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(s_running);
|
||||||
|
s_running = false;
|
||||||
|
shutdownSDL = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shutdownSDL)
|
||||||
{
|
{
|
||||||
m_running = false;
|
|
||||||
// wake the thread with a quit event if it's currently waiting for events
|
// wake the thread with a quit event if it's currently waiting for events
|
||||||
SDL_Event evt;
|
SDL_Event evt;
|
||||||
SDL_zero(evt);
|
SDL_zero(evt);
|
||||||
evt.type = SDL_EVENT_QUIT;
|
evt.type = SDL_EVENT_QUIT;
|
||||||
SDL_PushEvent(&evt);
|
SDL_PushEvent(&evt);
|
||||||
|
if (s_thread.joinable())
|
||||||
if (m_thread.joinable())
|
|
||||||
{
|
{
|
||||||
m_thread.join();
|
s_thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ControllerBase>> SDLControllerProvider::get_controllers()
|
std::vector<std::shared_ptr<ControllerBase>> SDLControllerProvider::get_controllers()
|
||||||
@ -47,34 +62,22 @@ std::vector<std::shared_ptr<ControllerBase>> SDLControllerProvider::get_controll
|
|||||||
std::unordered_map<SDL_GUID, size_t, SDL_JoystickGUIDHash> guid_counter;
|
std::unordered_map<SDL_GUID, size_t, SDL_JoystickGUIDHash> guid_counter;
|
||||||
|
|
||||||
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
|
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
|
||||||
|
|
||||||
int gamepad_count = 0;
|
int gamepad_count = 0;
|
||||||
|
|
||||||
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
|
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
|
||||||
|
|
||||||
if (gamepad_ids)
|
if (gamepad_ids)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < gamepad_count; ++i)
|
for (size_t i = 0; i < gamepad_count; ++i)
|
||||||
{
|
{
|
||||||
const auto guid = SDL_GetGamepadGUIDForID(gamepad_ids[i]);
|
const auto guid = SDL_GetGamepadGUIDForID(gamepad_ids[i]);
|
||||||
|
|
||||||
const auto it = guid_counter.try_emplace(guid, 0);
|
const auto it = guid_counter.try_emplace(guid, 0);
|
||||||
|
|
||||||
if (const char* name = SDL_GetGamepadNameForID(gamepad_ids[i]))
|
if (const char* name = SDL_GetGamepadNameForID(gamepad_ids[i]))
|
||||||
{
|
|
||||||
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second, name));
|
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second, name));
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second));
|
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second));
|
||||||
}
|
|
||||||
|
|
||||||
++it.first->second;
|
++it.first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_free(gamepad_ids);
|
SDL_free(gamepad_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,11 +85,8 @@ int SDLControllerProvider::get_index(size_t guid_index, const SDL_GUID& guid) co
|
|||||||
{
|
{
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
int gamepad_count = 0;
|
int gamepad_count = 0;
|
||||||
|
|
||||||
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
|
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
|
||||||
|
|
||||||
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
|
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
|
||||||
|
|
||||||
if (gamepad_ids)
|
if (gamepad_ids)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < gamepad_count; ++i)
|
for (size_t i = 0; i < gamepad_count; ++i)
|
||||||
@ -98,30 +98,23 @@ int SDLControllerProvider::get_index(size_t guid_index, const SDL_GUID& guid) co
|
|||||||
SDL_free(gamepad_ids);
|
SDL_free(gamepad_ids);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_free(gamepad_ids);
|
SDL_free(gamepad_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MotionSample SDLControllerProvider::motion_sample(SDL_JoystickID diid)
|
MotionSample SDLControllerProvider::motion_sample(SDL_JoystickID diid)
|
||||||
{
|
{
|
||||||
std::shared_lock lock(m_mutex);
|
std::shared_lock lock(s_mutex);
|
||||||
|
auto it = s_motion_states.find(diid);
|
||||||
auto it = m_motion_states.find(diid);
|
return (it != s_motion_states.end()) ? it->second.data : MotionSample{};
|
||||||
|
|
||||||
return (it != m_motion_states.end()) ? it->second.data : MotionSample{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLControllerProvider::event_thread()
|
void SDLControllerProvider::InitSDL()
|
||||||
{
|
{
|
||||||
SetThreadName("SDL_events");
|
|
||||||
|
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
||||||
@ -144,17 +137,30 @@ void SDLControllerProvider::event_thread()
|
|||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
|
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (m_running.load(std::memory_order_relaxed))
|
void SDLControllerProvider::ShutdownSDL()
|
||||||
|
{
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
void SDLControllerProvider::PumpSDLEvents()
|
||||||
|
{
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event))
|
||||||
|
HandleSDLEvent(event);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SDLControllerProvider::HandleSDLEvent(SDL_Event& event)
|
||||||
|
{
|
||||||
|
switch (event.type)
|
||||||
{
|
{
|
||||||
SDL_Event event{};
|
|
||||||
SDL_WaitEvent(&event);
|
|
||||||
|
|
||||||
switch (event.type)
|
|
||||||
{
|
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
{
|
{
|
||||||
m_running = false;
|
std::scoped_lock _l(s_mutex);
|
||||||
|
s_running = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION: /**< Game controller axis motion */
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION: /**< Game controller axis motion */
|
||||||
@ -171,13 +177,15 @@ void SDLControllerProvider::event_thread()
|
|||||||
}
|
}
|
||||||
case SDL_EVENT_GAMEPAD_ADDED: /**< A new Game controller has been inserted into the system */
|
case SDL_EVENT_GAMEPAD_ADDED: /**< A new Game controller has been inserted into the system */
|
||||||
{
|
{
|
||||||
|
std::scoped_lock _l(s_mutex);
|
||||||
InputManager::instance().on_device_changed();
|
InputManager::instance().on_device_changed();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_EVENT_GAMEPAD_REMOVED: /**< An opened Game controller has been removed */
|
case SDL_EVENT_GAMEPAD_REMOVED: /**< An opened Game controller has been removed */
|
||||||
{
|
{
|
||||||
|
std::scoped_lock _l(s_mutex);
|
||||||
InputManager::instance().on_device_changed();
|
InputManager::instance().on_device_changed();
|
||||||
m_motion_states.erase(event.gdevice.which);
|
s_motion_states.erase(event.gdevice.which);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_EVENT_GAMEPAD_REMAPPED: /**< The controller mapping was updated */
|
case SDL_EVENT_GAMEPAD_REMAPPED: /**< The controller mapping was updated */
|
||||||
@ -200,10 +208,8 @@ void SDLControllerProvider::event_thread()
|
|||||||
{
|
{
|
||||||
SDL_JoystickID id = event.gsensor.which;
|
SDL_JoystickID id = event.gsensor.which;
|
||||||
uint64_t ts = event.gsensor.timestamp;
|
uint64_t ts = event.gsensor.timestamp;
|
||||||
|
std::scoped_lock _l(s_mutex);
|
||||||
std::scoped_lock lock(m_mutex);
|
auto& state = s_motion_states[id];
|
||||||
|
|
||||||
auto& state = m_motion_states[id];
|
|
||||||
auto& tracking = state.tracking;
|
auto& tracking = state.tracking;
|
||||||
|
|
||||||
if (event.gsensor.sensor == SDL_SENSOR_ACCEL)
|
if (event.gsensor.sensor == SDL_SENSOR_ACCEL)
|
||||||
@ -274,7 +280,21 @@ void SDLControllerProvider::event_thread()
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
|
}
|
||||||
|
|
||||||
|
void SDLControllerProvider::event_thread()
|
||||||
|
{
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
cemu_assert(false);
|
||||||
|
#endif
|
||||||
|
SetThreadName("SDL_events");
|
||||||
|
InitSDL();
|
||||||
|
while (s_running.load(std::memory_order_relaxed))
|
||||||
|
{
|
||||||
|
SDL_Event event{};
|
||||||
|
SDL_WaitEvent(&event);
|
||||||
|
HandleSDLEvent(event);
|
||||||
|
}
|
||||||
|
ShutdownSDL();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,12 +28,26 @@ public:
|
|||||||
|
|
||||||
MotionSample motion_sample(SDL_JoystickID diid);
|
MotionSample motion_sample(SDL_JoystickID diid);
|
||||||
|
|
||||||
|
// exposed for manual event handling on macOS
|
||||||
|
#if BOOST_OS_MACOS
|
||||||
|
static void InitSDL();
|
||||||
|
static void ShutdownSDL();
|
||||||
|
static void PumpSDLEvents();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void event_thread();
|
void event_thread();
|
||||||
|
static void HandleSDLEvent(union SDL_Event& event);
|
||||||
|
#if !BOOST_OS_MACOS
|
||||||
|
static void InitSDL();
|
||||||
|
static void ShutdownSDL();
|
||||||
|
#endif
|
||||||
|
|
||||||
std::atomic_bool m_running = false;
|
// there is only one SDL instance, for this reason all of our state can be static
|
||||||
std::thread m_thread;
|
inline static std::atomic_int s_initCount{0};
|
||||||
mutable std::shared_mutex m_mutex;
|
inline static std::shared_mutex s_mutex;
|
||||||
|
inline static std::atomic_bool s_running = false;
|
||||||
|
inline static std::thread s_thread;
|
||||||
|
|
||||||
struct MotionInfoTracking
|
struct MotionInfoTracking
|
||||||
{
|
{
|
||||||
@ -55,5 +69,5 @@ private:
|
|||||||
MotionState() = default;
|
MotionState() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_map<SDL_JoystickID, MotionState> m_motion_states{};
|
inline static std::unordered_map<SDL_JoystickID, MotionState> s_motion_states{};
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user