From 569207761b721b40c22892c76354a30f74086833 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 13 Aug 2025 10:16:49 +0100 Subject: [PATCH 1/3] [network] use jthread and use std::vector for packet list instead of std::list Signed-off-by: lizzie --- src/network/announce_multiplayer_session.cpp | 103 ++++---- src/network/announce_multiplayer_session.h | 11 +- src/network/room.cpp | 98 ++++---- src/network/room_member.cpp | 251 +++++++++---------- 4 files changed, 219 insertions(+), 244 deletions(-) diff --git a/src/network/announce_multiplayer_session.cpp b/src/network/announce_multiplayer_session.cpp index d2a47de73d..5e4d59755f 100644 --- a/src/network/announce_multiplayer_session.cpp +++ b/src/network/announce_multiplayer_session.cpp @@ -19,9 +19,6 @@ namespace Core { -// Time between room is announced to web_service -static constexpr std::chrono::seconds announce_time_interval(15); - AnnounceMultiplayerSession::AnnounceMultiplayerSession() { #ifdef ENABLE_WEB_SERVICE backend = std::make_unique(Settings::values.web_api_url.GetValue(), @@ -53,18 +50,58 @@ WebService::WebResult AnnounceMultiplayerSession::Register() { } void AnnounceMultiplayerSession::Start() { - if (announce_multiplayer_thread) { + if (announce_multiplayer_thread.has_value()) { Stop(); } - shutdown_event.Reset(); - announce_multiplayer_thread = - std::make_unique(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this); + announce_multiplayer_thread.emplace([&](std::stop_token stoken) { + // Invokes all current bound error callbacks. + const auto ErrorCallback = [this](WebService::WebResult result) { + std::lock_guard lock(callback_mutex); + for (auto callback : error_callbacks) + (*callback)(result); + }; + + if (!registered) { + WebService::WebResult result = Register(); + if (result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(result); + return; + } + } + + // Time between room is announced to web_service + std::chrono::seconds const announce_timeslice(15); + auto update_time = std::chrono::steady_clock::now(); + std::future future; + while (!shutdown_event.WaitUntil(update_time)) { + update_time = std::chrono::steady_clock::now() + announce_timeslice; + auto room = Network::GetRoom().lock(); + if (!room) { + break; + } + if (room->GetState() != Network::Room::State::Open) { + break; + } + UpdateBackendData(room); + WebService::WebResult result = backend->Update(); + if (result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(result); + } + if (result.result_string == "404") { + registered = false; + // Needs to register the room again + WebService::WebResult register_result = Register(); + if (register_result.result_code != WebService::WebResult::Code::Success) { + ErrorCallback(register_result); + } + } + } + }); } void AnnounceMultiplayerSession::Stop() { - if (announce_multiplayer_thread) { + if (announce_multiplayer_thread.has_value()) { shutdown_event.Set(); - announce_multiplayer_thread->join(); announce_multiplayer_thread.reset(); backend->Delete(); registered = false; @@ -101,58 +138,10 @@ void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr future; - while (!shutdown_event.WaitUntil(update_time)) { - update_time += announce_time_interval; - auto room = Network::GetRoom().lock(); - if (!room) { - break; - } - if (room->GetState() != Network::Room::State::Open) { - break; - } - UpdateBackendData(room); - WebService::WebResult result = backend->Update(); - if (result.result_code != WebService::WebResult::Code::Success) { - ErrorCallback(result); - } - if (result.result_string == "404") { - registered = false; - // Needs to register the room again - WebService::WebResult register_result = Register(); - if (register_result.result_code != WebService::WebResult::Code::Success) { - ErrorCallback(register_result); - } - } - } -} - AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() { return backend->GetRoomList(); } -bool AnnounceMultiplayerSession::IsRunning() const { - return announce_multiplayer_thread != nullptr; -} - void AnnounceMultiplayerSession::UpdateCredentials() { ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running"); #ifdef ENABLE_WEB_SERVICE diff --git a/src/network/announce_multiplayer_session.h b/src/network/announce_multiplayer_session.h index 4e5cd33e1f..835423bfc1 100644 --- a/src/network/announce_multiplayer_session.h +++ b/src/network/announce_multiplayer_session.h @@ -72,7 +72,9 @@ public: /** * Whether the announce session is still running */ - bool IsRunning() const; + [[nodiscard]] bool IsRunning() const { + return announce_multiplayer_thread.has_value(); + } /** * Recreates the backend, updating the credentials. @@ -82,16 +84,13 @@ public: private: void UpdateBackendData(std::shared_ptr room); - void AnnounceMultiplayerLoop(); Common::Event shutdown_event; - std::mutex callback_mutex; std::set error_callbacks; - std::unique_ptr announce_multiplayer_thread; - + std::optional announce_multiplayer_thread; /// Backend interface that logs fields std::unique_ptr backend; - + std::mutex callback_mutex; std::atomic_bool registered = false; ///< Whether the room has been registered }; diff --git a/src/network/room.cpp b/src/network/room.cpp index 1a3ad75d2b..586f0b1257 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -1,6 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -54,13 +53,11 @@ public: RoomImpl() : random_gen(std::random_device()()) {} /// Thread that receives and dispatches network packets - std::unique_ptr room_thread; + std::optional room_thread; /// Verification backend of the room std::unique_ptr verify_backend; - /// Thread function that will receive and dispatch messages until the room is destroyed. - void ServerLoop(); void StartLoop(); /** @@ -240,59 +237,57 @@ public: }; // RoomImpl -void Room::RoomImpl::ServerLoop() { - while (state != State::Closed) { - ENetEvent event; - if (enet_host_service(server, &event, 5) > 0) { - switch (event.type) { - case ENET_EVENT_TYPE_RECEIVE: - switch (event.packet->data[0]) { - case IdJoinRequest: - HandleJoinRequest(&event); +void Room::RoomImpl::StartLoop() { + room_thread.emplace([&](std::stop_token stoken) { + while (state != State::Closed) { + ENetEvent event; + if (enet_host_service(server, &event, 5) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + switch (event.packet->data[0]) { + case IdJoinRequest: + HandleJoinRequest(&event); + break; + case IdSetGameInfo: + HandleGameInfoPacket(&event); + break; + case IdProxyPacket: + HandleProxyPacket(&event); + break; + case IdLdnPacket: + HandleLdnPacket(&event); + break; + case IdChatMessage: + HandleChatPacket(&event); + break; + // Moderation + case IdModKick: + HandleModKickPacket(&event); + break; + case IdModBan: + HandleModBanPacket(&event); + break; + case IdModUnban: + HandleModUnbanPacket(&event); + break; + case IdModGetBanList: + HandleModGetBanListPacket(&event); + break; + } + enet_packet_destroy(event.packet); break; - case IdSetGameInfo: - HandleGameInfoPacket(&event); + case ENET_EVENT_TYPE_DISCONNECT: + HandleClientDisconnection(event.peer); break; - case IdProxyPacket: - HandleProxyPacket(&event); - break; - case IdLdnPacket: - HandleLdnPacket(&event); - break; - case IdChatMessage: - HandleChatPacket(&event); - break; - // Moderation - case IdModKick: - HandleModKickPacket(&event); - break; - case IdModBan: - HandleModBanPacket(&event); - break; - case IdModUnban: - HandleModUnbanPacket(&event); - break; - case IdModGetBanList: - HandleModGetBanListPacket(&event); + case ENET_EVENT_TYPE_NONE: + case ENET_EVENT_TYPE_CONNECT: break; } - enet_packet_destroy(event.packet); - break; - case ENET_EVENT_TYPE_DISCONNECT: - HandleClientDisconnection(event.peer); - break; - case ENET_EVENT_TYPE_NONE: - case ENET_EVENT_TYPE_CONNECT: - break; } } - } - // Close the connection to all members: - SendCloseMessage(); -} - -void Room::RoomImpl::StartLoop() { - room_thread = std::make_unique(&Room::RoomImpl::ServerLoop, this); + // Close the connection to all members: + SendCloseMessage(); + }); } void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) { @@ -1132,7 +1127,6 @@ void Room::SetVerifyUID(const std::string& uid) { void Room::Destroy() { room_impl->state = State::Closed; - room_impl->room_thread->join(); room_impl->room_thread.reset(); if (room_impl->server) { diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index a6845273c5..886850b5c4 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,6 +20,21 @@ constexpr u32 ConnectionTimeoutMs = 5000; class RoomMember::RoomMemberImpl { public: + void SetState(const State new_state) noexcept { + if (state != new_state) { + state = new_state; + Invoke(state); + } + } + + void SetError(const Error new_error) noexcept { + Invoke(new_error); + } + + [[nodiscard]] bool IsConnected() const noexcept { + return state == State::Joining || state == State::Joined || state == State::Moderator; + } + ENetHost* client = nullptr; ///< ENet network interface. ENetPeer* server = nullptr; ///< The server peer the client is connected to @@ -30,9 +47,6 @@ public: GameInfo current_game_info; std::atomic state{State::Idle}; ///< Current state of the RoomMember. - void SetState(const State new_state); - void SetError(const Error new_error); - bool IsConnected() const; std::string nickname; ///< The nickname of this member. @@ -43,9 +57,9 @@ public: std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. /// Thread that receives and dispatches network packets - std::unique_ptr loop_thread; + std::optional loop_thread; std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable. - std::list send_list; ///< A list that stores all packets to send the async + std::vector send_list; ///< A list that stores all packets to send the async template using CallbackSet = std::set>; @@ -68,8 +82,6 @@ public: }; Callbacks callbacks; ///< All CallbackSets to all events - void MemberLoop(); - void StartLoop(); /** @@ -146,134 +158,117 @@ public: }; // RoomMemberImpl -void RoomMember::RoomMemberImpl::SetState(const State new_state) { - if (state != new_state) { - state = new_state; - Invoke(state); - } -} - -void RoomMember::RoomMemberImpl::SetError(const Error new_error) { - Invoke(new_error); -} - -bool RoomMember::RoomMemberImpl::IsConnected() const { - return state == State::Joining || state == State::Joined || state == State::Moderator; -} - -void RoomMember::RoomMemberImpl::MemberLoop() { - // Receive packets while the connection is open - while (IsConnected()) { - std::lock_guard lock(network_mutex); - ENetEvent event; - if (enet_host_service(client, &event, 5) > 0) { - switch (event.type) { - case ENET_EVENT_TYPE_RECEIVE: - switch (event.packet->data[0]) { - case IdProxyPacket: - HandleProxyPackets(&event); +void RoomMember::RoomMemberImpl::StartLoop() { + loop_thread.emplace([&](std::stop_token stoken) { + // Receive packets while the connection is open + while (IsConnected()) { + std::lock_guard lock(network_mutex); + ENetEvent event; + if (enet_host_service(client, &event, 5) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + switch (event.packet->data[0]) { + case IdProxyPacket: + HandleProxyPackets(&event); + break; + case IdLdnPacket: + HandleLdnPackets(&event); + break; + case IdChatMessage: + HandleChatPacket(&event); + break; + case IdStatusMessage: + HandleStatusMessagePacket(&event); + break; + case IdRoomInformation: + HandleRoomInformationPacket(&event); + break; + case IdJoinSuccess: + case IdJoinSuccessAsMod: + // The join request was successful, we are now in the room. + // If we joined successfully, there must be at least one client in the room: us. + ASSERT_MSG(member_information.size() > 0, + "We have not yet received member information."); + HandleJoinPacket(&event); // Get the MAC Address for the client + if (event.packet->data[0] == IdJoinSuccessAsMod) { + SetState(State::Moderator); + } else { + SetState(State::Joined); + } + break; + case IdModBanListResponse: + HandleModBanListResponsePacket(&event); + break; + case IdRoomIsFull: + SetState(State::Idle); + SetError(Error::RoomIsFull); + break; + case IdNameCollision: + SetState(State::Idle); + SetError(Error::NameCollision); + break; + case IdIpCollision: + SetState(State::Idle); + SetError(Error::IpCollision); + break; + case IdVersionMismatch: + SetState(State::Idle); + SetError(Error::WrongVersion); + break; + case IdWrongPassword: + SetState(State::Idle); + SetError(Error::WrongPassword); + break; + case IdCloseRoom: + SetState(State::Idle); + SetError(Error::LostConnection); + break; + case IdHostKicked: + SetState(State::Idle); + SetError(Error::HostKicked); + break; + case IdHostBanned: + SetState(State::Idle); + SetError(Error::HostBanned); + break; + case IdModPermissionDenied: + SetError(Error::PermissionDenied); + break; + case IdModNoSuchUser: + SetError(Error::NoSuchUser); + break; + } + enet_packet_destroy(event.packet); break; - case IdLdnPacket: - HandleLdnPackets(&event); - break; - case IdChatMessage: - HandleChatPacket(&event); - break; - case IdStatusMessage: - HandleStatusMessagePacket(&event); - break; - case IdRoomInformation: - HandleRoomInformationPacket(&event); - break; - case IdJoinSuccess: - case IdJoinSuccessAsMod: - // The join request was successful, we are now in the room. - // If we joined successfully, there must be at least one client in the room: us. - ASSERT_MSG(member_information.size() > 0, - "We have not yet received member information."); - HandleJoinPacket(&event); // Get the MAC Address for the client - if (event.packet->data[0] == IdJoinSuccessAsMod) { - SetState(State::Moderator); - } else { - SetState(State::Joined); + case ENET_EVENT_TYPE_DISCONNECT: + if (state == State::Joined || state == State::Moderator) { + SetState(State::Idle); + SetError(Error::LostConnection); } break; - case IdModBanListResponse: - HandleModBanListResponsePacket(&event); + case ENET_EVENT_TYPE_NONE: break; - case IdRoomIsFull: - SetState(State::Idle); - SetError(Error::RoomIsFull); - break; - case IdNameCollision: - SetState(State::Idle); - SetError(Error::NameCollision); - break; - case IdIpCollision: - SetState(State::Idle); - SetError(Error::IpCollision); - break; - case IdVersionMismatch: - SetState(State::Idle); - SetError(Error::WrongVersion); - break; - case IdWrongPassword: - SetState(State::Idle); - SetError(Error::WrongPassword); - break; - case IdCloseRoom: - SetState(State::Idle); - SetError(Error::LostConnection); - break; - case IdHostKicked: - SetState(State::Idle); - SetError(Error::HostKicked); - break; - case IdHostBanned: - SetState(State::Idle); - SetError(Error::HostBanned); - break; - case IdModPermissionDenied: - SetError(Error::PermissionDenied); - break; - case IdModNoSuchUser: - SetError(Error::NoSuchUser); + case ENET_EVENT_TYPE_CONNECT: + // The ENET_EVENT_TYPE_CONNECT event can not possibly happen here because we're + // already connected + ASSERT_MSG(false, "Received unexpected connect event while already connected"); break; } - enet_packet_destroy(event.packet); - break; - case ENET_EVENT_TYPE_DISCONNECT: - if (state == State::Joined || state == State::Moderator) { - SetState(State::Idle); - SetError(Error::LostConnection); - } - break; - case ENET_EVENT_TYPE_NONE: - break; - case ENET_EVENT_TYPE_CONNECT: - // The ENET_EVENT_TYPE_CONNECT event can not possibly happen here because we're - // already connected - ASSERT_MSG(false, "Received unexpected connect event while already connected"); - break; } + std::vector packets; + { + std::lock_guard send_lock(send_list_mutex); + packets.swap(send_list); + } + for (auto const& packet : packets) { + ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(), + ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(server, 0, enetPacket); + } + enet_host_flush(client); } - std::list packets; - { - std::lock_guard send_lock(send_list_mutex); - packets.swap(send_list); - } - for (const auto& packet : packets) { - ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(), - ENET_PACKET_FLAG_RELIABLE); - enet_peer_send(server, 0, enetPacket); - } - enet_host_flush(client); - } - Disconnect(); -}; - -void RoomMember::RoomMemberImpl::StartLoop() { - loop_thread = std::make_unique(&RoomMember::RoomMemberImpl::MemberLoop, this); + Disconnect(); + }); } void RoomMember::RoomMemberImpl::Send(Packet&& packet) { @@ -747,9 +742,7 @@ void RoomMember::Unbind(CallbackHandle handle) { void RoomMember::Leave() { room_member_impl->SetState(State::Idle); - room_member_impl->loop_thread->join(); room_member_impl->loop_thread.reset(); - enet_host_destroy(room_member_impl->client); room_member_impl->client = nullptr; } -- 2.39.5 From c6ba8e5c3917a014d5e0cc887719bb99bceeda85 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 08:39:54 +0000 Subject: [PATCH 2/3] [network] fix android build of jthread Signed-off-by: lizzie --- src/network/room.cpp | 1 + src/network/room_member.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/network/room.cpp b/src/network/room.cpp index 586f0b1257..dae722a854 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -12,6 +12,7 @@ #include #include #include +#include "common/polyfill_thread.h" #include "common/logging/log.h" #include "enet/enet.h" #include "network/packet.h" diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp index 886850b5c4..52e1b23c46 100644 --- a/src/network/room_member.cpp +++ b/src/network/room_member.cpp @@ -9,6 +9,7 @@ #include #include #include "common/assert.h" +#include "common/polyfill_thread.h" #include "common/socket_types.h" #include "enet/enet.h" #include "network/packet.h" -- 2.39.5 From 1ec60c5613fac308e63323afb35c0e06c5b5976a Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 6 Sep 2025 08:01:34 +0000 Subject: [PATCH 3/3] [qt] use std::jthread instead of playing around with std::thread Signed-off-by: lizzie --- .../calibration_configuration_job.cpp | 15 ++-- src/yuzu/applets/qt_software_keyboard.cpp | 71 ++++++++---------- src/yuzu/applets/qt_software_keyboard.h | 10 +-- src/yuzu/applets/qt_web_browser.cpp | 72 ++++++++----------- src/yuzu/applets/qt_web_browser.h | 21 ++---- src/yuzu/util/overlay_dialog.cpp | 38 ++++------ src/yuzu/util/overlay_dialog.h | 11 +-- 7 files changed, 89 insertions(+), 149 deletions(-) diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp index 8f5466253c..02ffa880aa 100644 --- a/src/tests/input_common/calibration_configuration_job.cpp +++ b/src/tests/input_common/calibration_configuration_job.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,13 +21,9 @@ public: : socket(io_context, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {} ~FakeCemuhookServer() { - is_running = false; boost::system::error_code error_code; socket.shutdown(boost::asio::socket_base::shutdown_both, error_code); socket.close(); - if (handler.joinable()) { - handler.join(); - } } u16 GetPort() { @@ -41,10 +40,9 @@ public: sizeof(InputCommon::CemuhookUDP::Message); REQUIRE(touch_movement_path.size() > 0); - is_running = true; - handler = std::thread([touch_movement_path, this]() { + handler = std::jthread([touch_movement_path, this](std::stop_token stoken) { auto current_touch_position = touch_movement_path.begin(); - while (is_running) { + while (!stoken.stop_requested()) { boost::asio::ip::udp::endpoint sender_endpoint; boost::system::error_code error_code; auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer), @@ -87,8 +85,7 @@ private: boost::asio::ip::udp::socket socket; std::array send_buffer; std::array receive_buffer; - bool is_running = false; - std::thread handler; + std::jthread handler; }; TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") { diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index 2749e6ed31..ddaf1fb9ba 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -1492,53 +1494,36 @@ void QtSoftwareKeyboardDialog::MoveTextCursorDirection(Direction direction) { } void QtSoftwareKeyboardDialog::StartInputThread() { - if (input_thread_running) { - return; - } - - input_thread_running = true; - - input_thread = std::thread(&QtSoftwareKeyboardDialog::InputThread, this); + input_thread = std::jthread([&](std::stop_token stoken) { + while (!stoken.stop_requested()) { + input_interpreter->PollInput(); + HandleButtonPressedOnce< + Core::HID::NpadButton::A, Core::HID::NpadButton::B, Core::HID::NpadButton::X, + Core::HID::NpadButton::Y, Core::HID::NpadButton::StickL, Core::HID::NpadButton::StickR, + Core::HID::NpadButton::L, Core::HID::NpadButton::R, Core::HID::NpadButton::Plus, + Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, + Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, + Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, + Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft, + Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight, + Core::HID::NpadButton::StickRDown>(); + HandleButtonHold(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + }); } void QtSoftwareKeyboardDialog::StopInputThread() { - input_thread_running = false; - - if (input_thread.joinable()) { - input_thread.join(); - } - - if (input_interpreter) { + input_thread.request_stop(); + if (input_interpreter) input_interpreter->ResetButtonStates(); - } -} - -void QtSoftwareKeyboardDialog::InputThread() { - while (input_thread_running) { - input_interpreter->PollInput(); - - HandleButtonPressedOnce< - Core::HID::NpadButton::A, Core::HID::NpadButton::B, Core::HID::NpadButton::X, - Core::HID::NpadButton::Y, Core::HID::NpadButton::StickL, Core::HID::NpadButton::StickR, - Core::HID::NpadButton::L, Core::HID::NpadButton::R, Core::HID::NpadButton::Plus, - Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, - Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, - Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, - Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft, - Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight, - Core::HID::NpadButton::StickRDown>(); - - HandleButtonHold(); - - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } } QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h index 7e2fdf09ea..217d305ffc 100644 --- a/src/yuzu/applets/qt_software_keyboard.h +++ b/src/yuzu/applets/qt_software_keyboard.h @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -182,9 +184,6 @@ private: void StartInputThread(); void StopInputThread(); - /// The thread where input is being polled and processed. - void InputThread(); - std::unique_ptr ui; Core::System& system; @@ -220,10 +219,7 @@ private: std::atomic caps_lock_enabled{false}; std::unique_ptr input_interpreter; - - std::thread input_thread; - - std::atomic input_thread_running{}; + std::jthread input_thread; }; class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index a287ea16df..3a01b0bc17 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -282,54 +285,41 @@ void QtNXWebEngineView::SendKeyPressEvent(int key) { } void QtNXWebEngineView::StartInputThread() { - if (input_thread_running) { - return; - } + input_thread = std::jthread([&](std::stop_token stoken) { + // Wait for 1 second before allowing any inputs to be processed. + std::this_thread::sleep_for(std::chrono::seconds(1)); + if (is_local) { + QWidget::grabKeyboard(); + } + while (!stoken.stop_requested()) { + input_interpreter->PollInput(); - input_thread_running = true; - input_thread = std::thread(&QtNXWebEngineView::InputThread, this); + HandleWindowFooterButtonPressedOnce(); + + HandleWindowKeyButtonPressedOnce< + Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, + Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, + Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, + Core::HID::NpadButton::StickLDown>(); + + HandleWindowKeyButtonHold< + Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, + Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, + Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, + Core::HID::NpadButton::StickLDown>(); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + }); } void QtNXWebEngineView::StopInputThread() { if (is_local) { QWidget::releaseKeyboard(); } - - input_thread_running = false; - if (input_thread.joinable()) { - input_thread.join(); - } -} - -void QtNXWebEngineView::InputThread() { - // Wait for 1 second before allowing any inputs to be processed. - std::this_thread::sleep_for(std::chrono::seconds(1)); - - if (is_local) { - QWidget::grabKeyboard(); - } - - while (input_thread_running) { - input_interpreter->PollInput(); - - HandleWindowFooterButtonPressedOnce(); - - HandleWindowKeyButtonPressedOnce< - Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, - Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, - Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, - Core::HID::NpadButton::StickLDown>(); - - HandleWindowKeyButtonHold< - Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right, - Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft, - Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight, - Core::HID::NpadButton::StickLDown>(); - - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } + input_thread.request_stop(); } void QtNXWebEngineView::LoadExtractedFonts() { diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h index e8a0b6931b..773726f1ac 100644 --- a/src/yuzu/applets/qt_web_browser.h +++ b/src/yuzu/applets/qt_web_browser.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -155,9 +158,6 @@ private: void StartInputThread(); void StopInputThread(); - /// The thread where input is being polled and processed. - void InputThread(); - /// Loads the extracted fonts using JavaScript. void LoadExtractedFonts(); @@ -165,24 +165,13 @@ private: void FocusFirstLinkElement(); InputCommon::InputSubsystem* input_subsystem; - std::unique_ptr url_interceptor; - std::unique_ptr input_interpreter; - - std::thread input_thread; - - std::atomic input_thread_running{}; - + std::jthread input_thread; std::atomic finished{}; - - Service::AM::Frontend::WebExitReason exit_reason{ - Service::AM::Frontend::WebExitReason::EndButtonPressed}; - + Service::AM::Frontend::WebExitReason exit_reason{Service::AM::Frontend::WebExitReason::EndButtonPressed}; std::string last_url{"http://localhost/"}; - bool is_local{}; - QWebEngineProfile* default_profile; QWebEngineSettings* global_settings; }; diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp index 466bbe7b25..01b894f744 100644 --- a/src/yuzu/util/overlay_dialog.cpp +++ b/src/yuzu/util/overlay_dialog.cpp @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -231,34 +233,20 @@ void OverlayDialog::TranslateButtonPress(Core::HID::NpadButton button) { } void OverlayDialog::StartInputThread() { - if (input_thread_running) { - return; - } - - input_thread_running = true; - - input_thread = std::thread(&OverlayDialog::InputThread, this); + input_thread = std::jthread([&](std::stop_token stoken) { + while (!stoken.stop_requested()) { + input_interpreter->PollInput(); + HandleButtonPressedOnce(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + }); } void OverlayDialog::StopInputThread() { - input_thread_running = false; - - if (input_thread.joinable()) { - input_thread.join(); - } -} - -void OverlayDialog::InputThread() { - while (input_thread_running) { - input_interpreter->PollInput(); - - HandleButtonPressedOnce(); - - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } + input_thread.request_stop(); } void OverlayDialog::keyPressEvent(QKeyEvent* e) { diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h index 62f9da311d..36c9b52339 100644 --- a/src/yuzu/util/overlay_dialog.h +++ b/src/yuzu/util/overlay_dialog.h @@ -1,9 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include #include #include @@ -91,9 +92,6 @@ private: void StartInputThread(); void StopInputThread(); - - /// The thread where input is being polled and processed. - void InputThread(); void keyPressEvent(QKeyEvent* e) override; std::unique_ptr ui; @@ -101,8 +99,5 @@ private: bool use_rich_text; std::unique_ptr input_interpreter; - - std::thread input_thread; - - std::atomic input_thread_running{}; + std::jthread input_thread; }; -- 2.39.5