Fix several issues discussed in the PR comments. The macOS main thread issue is still open.

This commit is contained in:
lijunyu-cn 2026-04-12 20:54:26 +08:00
parent 3a9de5736e
commit 0d52fb7834
9 changed files with 165 additions and 71 deletions

View File

@ -391,6 +391,7 @@ int CemuApp::OnExit()
{
wxApp::OnExit();
wxTheClipboard->Flush();
InputManager::instance().Shutdown();
#if BOOST_OS_WINDOWS
ExitProcess(0);
#else

View File

@ -53,8 +53,7 @@ InputManager::InputManager()
InputManager::~InputManager()
{
m_update_thread_shutdown.store(true);
m_update_thread.join();
// destructors will not invoked forever, so we manually release resources in Shutdown().
}
void InputManager::load() noexcept
@ -952,3 +951,21 @@ void InputManager::update_thread()
std::this_thread::yield();
}
}
void InputManager::Shutdown()
{
m_update_thread_shutdown = true;
if (m_update_thread.joinable())
{
m_update_thread.join();
}
for (auto& pad : m_vpad) pad.reset();
for (auto& pad : m_wpad) pad.reset();
for (auto& providers : m_api_available)
{
providers.clear();
}
}

View File

@ -46,6 +46,8 @@ public:
bool is_gameprofile_set(size_t player_index) const;
void Shutdown();
EmulatedControllerPtr set_controller(EmulatedControllerPtr controller);
EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type);
EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type, const std::shared_ptr<ControllerBase>& controller);

View File

@ -42,8 +42,25 @@ DSUControllerProvider::~DSUControllerProvider()
if (m_running)
{
m_running = false;
m_writer_thread.join();
m_reader_thread.join();
boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_both, ec);
m_socket.close(ec);
if (m_reader_thread.joinable())
{
m_reader_thread.join();
}
{
std::scoped_lock lock(m_writer_mutex);
m_writer_cond.notify_all();
}
if (m_writer_thread.joinable())
{
m_writer_thread.join();
}
}
}
@ -261,6 +278,11 @@ void DSUControllerProvider::reader_thread()
const size_t len = m_socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, ec);
if (ec)
{
if (!m_running.load(std::memory_order_relaxed))
{
break;
}
#ifdef DEBUG_DSU_CLIENT
printf(" DSUControllerProvider::ReaderThread: exception %s\n", ec.what());
#endif

View File

@ -16,30 +16,6 @@ struct SDL_JoystickGUIDHash
SDLControllerProvider::SDLControllerProvider()
{
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1");
if (!SDL_Init(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC))
{
throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError()));
}
SDL_SetGamepadEventsEnabled(true);
if (!SDL_GamepadEventsEnabled())
{
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
}
m_motion_states.reserve(8);
m_running = true;
@ -53,13 +29,15 @@ SDLControllerProvider::~SDLControllerProvider()
m_running = false;
// wake the thread with a quit event if it's currently waiting for events
SDL_Event evt;
SDL_zero(evt);
evt.type = SDL_EVENT_QUIT;
SDL_PushEvent(&evt);
// wait until thread exited
m_thread.join();
}
SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
if (m_thread.joinable())
{
m_thread.join();
}
}
}
std::vector<std::shared_ptr<ControllerBase>> SDLControllerProvider::get_controllers()
@ -143,6 +121,30 @@ MotionSample SDLControllerProvider::motion_sample(SDL_JoystickID diid)
void SDLControllerProvider::event_thread()
{
SetThreadName("SDL_events");
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1");
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC))
{
throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError()));
}
SDL_SetGamepadEventsEnabled(true);
if (!SDL_GamepadEventsEnabled())
{
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
}
while (m_running.load(std::memory_order_relaxed))
{
SDL_Event event{};
@ -153,9 +155,8 @@ void SDLControllerProvider::event_thread()
case SDL_EVENT_QUIT:
{
m_running = false;
return;
break;
}
case SDL_EVENT_GAMEPAD_AXIS_MOTION: /**< Game controller axis motion */
{
break;
@ -275,4 +276,5 @@ void SDLControllerProvider::event_thread()
}
}
}
SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
}

View File

@ -30,11 +30,11 @@ public:
private:
void event_thread();
std::atomic_bool m_running = false;
std::thread m_thread;
mutable std::shared_mutex m_mutex;
struct MotionInfoTracking
{
uint64 lastTimestampGyro{};

View File

@ -25,9 +25,31 @@ WiimoteControllerProvider::~WiimoteControllerProvider()
if (m_running)
{
m_running = false;
m_writer_thread.join();
m_reader_thread.join();
m_connectionThread.join();
{
std::scoped_lock lock(m_writer_mutex);
m_writer_cond.notify_all();
}
{
std::scoped_lock lock(m_connectionMutex);
m_connectionCond.notify_all();
}
if (m_writer_thread.joinable())
{
m_writer_thread.join();
}
if (m_reader_thread.joinable())
{
m_reader_thread.join();
}
if (m_connectionThread.joinable())
{
m_connectionThread.join();
}
}
}
@ -169,7 +191,8 @@ void WiimoteControllerProvider::connectionThread()
m_connectedDevices.clear();
std::ranges::move(devices, std::back_inserter(m_connectedDevices));
}
std::this_thread::sleep_for(std::chrono::seconds(2));
std::unique_lock<std::mutex> lock(m_connectionMutex);
m_connectionCond.wait_for(lock, std::chrono::seconds(2));
}
}

View File

@ -80,6 +80,8 @@ private:
std::shared_mutex m_device_mutex;
std::thread m_connectionThread;
std::mutex m_connectionMutex;
std::condition_variable m_connectionCond;
std::vector<WiimoteDevicePtr> m_connectedDevices;
std::mutex m_connectedDeviceMutex;
struct Wiimote

View File

@ -4,37 +4,62 @@
class ScreenSaver
{
public:
static void SetInhibit(bool inhibit)
{
// temporary workaround because feature crashes on macOS
static void SetInhibit(bool inhibit)
{
// temporary workaround because feature crashes on macOS
#if BOOST_OS_MACOS
return;
return;
#endif
// Initialize video subsystem if necessary
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
{
int initErr = SDL_InitSubSystem(SDL_INIT_VIDEO);
if (initErr)
{
cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error)");
}
}
// Toggle SDL's screen saver inhibition
if (inhibit)
{
SDL_DisableScreenSaver();
if (SDL_ScreenSaverEnabled())
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned SDL_TRUE)");
}
}
else
{
SDL_EnableScreenSaver();
if (!SDL_ScreenSaverEnabled())
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned SDL_FALSE)");
}
}
};
bool* inhibitArg = new bool(inhibit);
if (!SDL_RunOnMainThread(SetInhibitCallback, inhibitArg, false))
{
delete inhibitArg;
cemuLog_log(LogType::Force, "Failed to schedule screen saver logic on main thread: {}", SDL_GetError());
}
}
private:
static void SDLCALL SetInhibitCallback(void* userdata)
{
if (!userdata)
{
return;
}
bool inhibit = *static_cast<bool*>(userdata);
SDL_free(userdata);
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
{
if (!SDL_InitSubSystem(SDL_INIT_VIDEO))
{
cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error: {})", SDL_GetError());
return;
}
}
if (inhibit)
{
if (!SDL_DisableScreenSaver())
{
cemuLog_log(LogType::Force, "Could not disable screen saver: {}", SDL_GetError());
}
else if (SDL_ScreenSaverEnabled())
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned true)");
}
}
else
{
if (!SDL_EnableScreenSaver())
{
cemuLog_log(LogType::Force, "Could not enable screen saver: {}", SDL_GetError());
}
else if (!SDL_ScreenSaverEnabled())
{
cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned false)");
}
}
}
};