mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Add cemu hook changes related to PR #4609
This commit is contained in:
		
							parent
							
								
									0774b17846
								
							
						
					
					
						commit
						6ee8eab670
					
				| @ -397,8 +397,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | ||||
|                     std::tie(motion_devices[e].accel, motion_devices[e].gyro, | ||||
|                              motion_devices[e].rotation, motion_devices[e].orientation) = | ||||
|                         device->GetStatus(); | ||||
|                     sixaxis_at_rest = | ||||
|                         sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.00005f; | ||||
|                     sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| #include "input_common/main.h" | ||||
| #include "input_common/motion_emu.h" | ||||
| #include "input_common/touch_from_button.h" | ||||
| #include "input_common/udp/client.h" | ||||
| #include "input_common/udp/udp.h" | ||||
| #ifdef HAVE_SDL2 | ||||
| #include "input_common/sdl/sdl.h" | ||||
| @ -40,7 +41,11 @@ struct InputSubsystem::Impl { | ||||
|         sdl = SDL::Init(); | ||||
| #endif | ||||
| 
 | ||||
|         udp = CemuhookUDP::Init(); | ||||
|         udp = std::make_shared<InputCommon::CemuhookUDP::Client>(); | ||||
|         udpmotion = std::make_shared<UDPMotionFactory>(udp); | ||||
|         Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion); | ||||
|         udptouch = std::make_shared<UDPTouchFactory>(udp); | ||||
|         Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch); | ||||
|     } | ||||
| 
 | ||||
|     void Shutdown() { | ||||
| @ -53,12 +58,17 @@ struct InputSubsystem::Impl { | ||||
| #ifdef HAVE_SDL2 | ||||
|         sdl.reset(); | ||||
| #endif | ||||
|         udp.reset(); | ||||
|         Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); | ||||
|         Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); | ||||
| 
 | ||||
|         gcbuttons.reset(); | ||||
|         gcanalog.reset(); | ||||
| 
 | ||||
|         Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); | ||||
|         Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); | ||||
| 
 | ||||
|         udpmotion.reset(); | ||||
|         udptouch.reset(); | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { | ||||
| @ -109,14 +119,28 @@ struct InputSubsystem::Impl { | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     [[nodiscard]] MotionMapping GetMotionMappingForDevice( | ||||
|         const Common::ParamPackage& params) const { | ||||
|         if (!params.Has("class") || params.Get("class", "") == "any") { | ||||
|             return {}; | ||||
|         } | ||||
|         if (params.Get("class", "") == "cemuhookudp") { | ||||
|             // TODO return the correct motion device
 | ||||
|             return {}; | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     std::shared_ptr<Keyboard> keyboard; | ||||
|     std::shared_ptr<MotionEmu> motion_emu; | ||||
| #ifdef HAVE_SDL2 | ||||
|     std::unique_ptr<SDL::State> sdl; | ||||
| #endif | ||||
|     std::unique_ptr<CemuhookUDP::State> udp; | ||||
|     std::shared_ptr<GCButtonFactory> gcbuttons; | ||||
|     std::shared_ptr<GCAnalogFactory> gcanalog; | ||||
|     std::shared_ptr<UDPMotionFactory> udpmotion; | ||||
|     std::shared_ptr<UDPTouchFactory> udptouch; | ||||
|     std::shared_ptr<CemuhookUDP::Client> udp; | ||||
| }; | ||||
| 
 | ||||
| InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} | ||||
| @ -175,6 +199,22 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const { | ||||
|     return impl->gcbuttons.get(); | ||||
| } | ||||
| 
 | ||||
| UDPMotionFactory* InputSubsystem::GetUDPMotions() { | ||||
|     return impl->udpmotion.get(); | ||||
| } | ||||
| 
 | ||||
| const UDPMotionFactory* InputSubsystem::GetUDPMotions() const { | ||||
|     return impl->udpmotion.get(); | ||||
| } | ||||
| 
 | ||||
| UDPTouchFactory* InputSubsystem::GetUDPTouch() { | ||||
|     return impl->udptouch.get(); | ||||
| } | ||||
| 
 | ||||
| const UDPTouchFactory* InputSubsystem::GetUDPTouch() const { | ||||
|     return impl->udptouch.get(); | ||||
| } | ||||
| 
 | ||||
| void InputSubsystem::ReloadInputDevices() { | ||||
|     if (!impl->udp) { | ||||
|         return; | ||||
|  | ||||
| @ -54,6 +54,8 @@ public: | ||||
| 
 | ||||
| class GCAnalogFactory; | ||||
| class GCButtonFactory; | ||||
| class UDPMotionFactory; | ||||
| class UDPTouchFactory; | ||||
| class Keyboard; | ||||
| class MotionEmu; | ||||
| 
 | ||||
| @ -123,6 +125,18 @@ public: | ||||
|     /// Retrieves the underlying GameCube button handler.
 | ||||
|     [[nodiscard]] const GCButtonFactory* GetGCButtons() const; | ||||
| 
 | ||||
|     /// Retrieves the underlying udp motion handler.
 | ||||
|     [[nodiscard]] UDPMotionFactory* GetUDPMotions(); | ||||
| 
 | ||||
|     /// Retrieves the underlying udp motion handler.
 | ||||
|     [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const; | ||||
| 
 | ||||
|     /// Retrieves the underlying udp touch handler.
 | ||||
|     [[nodiscard]] UDPTouchFactory* GetUDPTouch(); | ||||
| 
 | ||||
|     /// Retrieves the underlying udp touch handler.
 | ||||
|     [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const; | ||||
| 
 | ||||
|     /// Reloads the input devices
 | ||||
|     void ReloadInputDevices(); | ||||
| 
 | ||||
|  | ||||
| @ -2,14 +2,13 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <array> | ||||
| #include <chrono> | ||||
| #include <cstring> | ||||
| #include <functional> | ||||
| #include <thread> | ||||
| #include <boost/asio.hpp> | ||||
| #include "common/logging/log.h" | ||||
| #include "core/settings.h" | ||||
| #include "input_common/udp/client.h" | ||||
| #include "input_common/udp/protocol.h" | ||||
| 
 | ||||
| @ -131,21 +130,60 @@ static void SocketLoop(Socket* socket) { | ||||
|     socket->Loop(); | ||||
| } | ||||
| 
 | ||||
| Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, | ||||
|                u8 pad_index, u32 client_id) | ||||
|     : status(std::move(status)) { | ||||
|     StartCommunication(host, port, pad_index, client_id); | ||||
| Client::Client() { | ||||
|     LOG_INFO(Input, "Udp Initialization started"); | ||||
|     for (std::size_t client = 0; client < clients.size(); client++) { | ||||
|         u8 pad = client % 4; | ||||
|         StartCommunication(client, Settings::values.udp_input_address, | ||||
|                            Settings::values.udp_input_port, pad, 24872); | ||||
|         // Set motion parameters
 | ||||
|         // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
 | ||||
|         // Real HW values are unkown, 0.0001 is an aproximate to Standard
 | ||||
|         clients[client].motion.SetGyroThreshold(0.0001f); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| Client::~Client() { | ||||
|     socket->Stop(); | ||||
|     thread.join(); | ||||
|     Reset(); | ||||
| } | ||||
| 
 | ||||
| std::vector<Common::ParamPackage> Client::GetInputDevices() const { | ||||
|     std::vector<Common::ParamPackage> devices; | ||||
|     for (std::size_t client = 0; client < clients.size(); client++) { | ||||
|         if (!DeviceConnected(client)) { | ||||
|             continue; | ||||
|         } | ||||
|         std::string name = fmt::format("UDP Controller{} {} {}", clients[client].active, | ||||
|                                        clients[client].active == 1, client); | ||||
|         devices.emplace_back(Common::ParamPackage{ | ||||
|             {"class", "cemuhookudp"}, | ||||
|             {"display", std::move(name)}, | ||||
|             {"port", std::to_string(client)}, | ||||
|         }); | ||||
|     } | ||||
|     return devices; | ||||
| } | ||||
| 
 | ||||
| bool Client::DeviceConnected(std::size_t pad) const { | ||||
|     // Use last timestamp to detect if the socket has stopped sending data
 | ||||
|     const auto now = std::chrono::system_clock::now(); | ||||
|     u64 time_difference = | ||||
|         std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update) | ||||
|             .count(); | ||||
|     return time_difference < 1000 && clients[pad].active == 1; | ||||
| } | ||||
| 
 | ||||
| void Client::ReloadUDPClient() { | ||||
|     for (std::size_t client = 0; client < clients.size(); client++) { | ||||
|         ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client); | ||||
|     } | ||||
| } | ||||
| void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { | ||||
|     socket->Stop(); | ||||
|     thread.join(); | ||||
|     StartCommunication(host, port, pad_index, client_id); | ||||
|     // client number must be determined from host / port and pad index
 | ||||
|     std::size_t client = pad_index; | ||||
|     clients[client].socket->Stop(); | ||||
|     clients[client].thread.join(); | ||||
|     StartCommunication(client, host, port, pad_index, client_id); | ||||
| } | ||||
| 
 | ||||
| void Client::OnVersion(Response::Version data) { | ||||
| @ -157,31 +195,39 @@ void Client::OnPortInfo(Response::PortInfo data) { | ||||
| } | ||||
| 
 | ||||
| void Client::OnPadData(Response::PadData data) { | ||||
|     // client number must be determined from host / port and pad index
 | ||||
|     std::size_t client = data.info.id; | ||||
|     LOG_TRACE(Input, "PadData packet received"); | ||||
|     if (data.packet_counter <= packet_sequence) { | ||||
|     if (data.packet_counter == clients[client].packet_sequence) { | ||||
|         LOG_WARNING( | ||||
|             Input, | ||||
|             "PadData packet dropped because its stale info. Current count: {} Packet count: {}", | ||||
|             packet_sequence, data.packet_counter); | ||||
|             clients[client].packet_sequence, data.packet_counter); | ||||
|         return; | ||||
|     } | ||||
|     packet_sequence = data.packet_counter; | ||||
|     // TODO: Check how the Switch handles motions and how the CemuhookUDP motion
 | ||||
|     // directions correspond to the ones of the Switch
 | ||||
|     Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z); | ||||
|     Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll); | ||||
|     clients[client].active = data.info.is_pad_active; | ||||
|     clients[client].packet_sequence = data.packet_counter; | ||||
|     const auto now = std::chrono::system_clock::now(); | ||||
|     u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>( | ||||
|                               now - clients[client].last_motion_update) | ||||
|                               .count(); | ||||
|     clients[client].last_motion_update = now; | ||||
|     Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw}; | ||||
|     clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y}); | ||||
|     // Gyroscope values are not it the correct scale from better joy.
 | ||||
|     // By dividing by 312 allow us to make one full turn = 1 turn
 | ||||
|     // This must be a configurable valued called sensitivity
 | ||||
|     clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f); | ||||
|     clients[client].motion.UpdateRotation(time_difference); | ||||
|     clients[client].motion.UpdateOrientation(time_difference); | ||||
|     Common::Vec3f gyroscope = clients[client].motion.GetGyroscope(); | ||||
|     Common::Vec3f accelerometer = clients[client].motion.GetAcceleration(); | ||||
|     Common::Vec3f rotation = clients[client].motion.GetRotations(); | ||||
|     std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation(); | ||||
| 
 | ||||
|     // TODO: Calculate the correct rotation vector and orientation matrix
 | ||||
|     const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f); | ||||
|     const std::array orientation{ | ||||
|         Common::Vec3f(1.0f, 0.0f, 0.0f), | ||||
|         Common::Vec3f(0.0f, 1.0f, 0.0f), | ||||
|         Common::Vec3f(0.0f, 0.0f, 1.0f), | ||||
|     }; | ||||
|     { | ||||
|         std::lock_guard guard(status->update_mutex); | ||||
| 
 | ||||
|         status->motion_status = {accel, gyro, rotation, orientation}; | ||||
|         std::lock_guard guard(clients[client].status.update_mutex); | ||||
|         clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation}; | ||||
| 
 | ||||
|         // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
 | ||||
|         // between a simple "tap" and a hard press that causes the touch screen to click.
 | ||||
| @ -190,11 +236,11 @@ void Client::OnPadData(Response::PadData data) { | ||||
|         float x = 0; | ||||
|         float y = 0; | ||||
| 
 | ||||
|         if (is_active && status->touch_calibration) { | ||||
|             const u16 min_x = status->touch_calibration->min_x; | ||||
|             const u16 max_x = status->touch_calibration->max_x; | ||||
|             const u16 min_y = status->touch_calibration->min_y; | ||||
|             const u16 max_y = status->touch_calibration->max_y; | ||||
|         if (is_active && clients[client].status.touch_calibration) { | ||||
|             const u16 min_x = clients[client].status.touch_calibration->min_x; | ||||
|             const u16 max_x = clients[client].status.touch_calibration->max_x; | ||||
|             const u16 min_y = clients[client].status.touch_calibration->min_y; | ||||
|             const u16 max_y = clients[client].status.touch_calibration->max_y; | ||||
| 
 | ||||
|             x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) / | ||||
|                 static_cast<float>(max_x - min_x); | ||||
| @ -202,17 +248,82 @@ void Client::OnPadData(Response::PadData data) { | ||||
|                 static_cast<float>(max_y - min_y); | ||||
|         } | ||||
| 
 | ||||
|         status->touch_status = {x, y, is_active}; | ||||
|         clients[client].status.touch_status = {x, y, is_active}; | ||||
| 
 | ||||
|         if (configuring) { | ||||
|             UpdateYuzuSettings(client, accelerometer, gyroscope, is_active); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) { | ||||
| void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, | ||||
|                                 u32 client_id) { | ||||
|     SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, | ||||
|                             [this](Response::PortInfo info) { OnPortInfo(info); }, | ||||
|                             [this](Response::PadData data) { OnPadData(data); }}; | ||||
|     LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); | ||||
|     socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); | ||||
|     thread = std::thread{SocketLoop, this->socket.get()}; | ||||
|     clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); | ||||
|     clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()}; | ||||
| } | ||||
| 
 | ||||
| void Client::Reset() { | ||||
|     for (std::size_t client = 0; client < clients.size(); client++) { | ||||
|         clients[client].socket->Stop(); | ||||
|         clients[client].thread.join(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, | ||||
|                                 const Common::Vec3<float>& gyro, bool touch) { | ||||
|     if (configuring) { | ||||
|         UDPPadStatus pad; | ||||
|         if (touch) { | ||||
|             pad.touch = PadTouch::Click; | ||||
|             pad_queue[client].Push(pad); | ||||
|         } | ||||
|         for (size_t i = 0; i < 3; ++i) { | ||||
|             if (gyro[i] > 6.0f || gyro[i] < -6.0f) { | ||||
|                 pad.motion = static_cast<PadMotion>(i); | ||||
|                 pad.motion_value = gyro[i]; | ||||
|                 pad_queue[client].Push(pad); | ||||
|             } | ||||
|             if (acc[i] > 2.0f || acc[i] < -2.0f) { | ||||
|                 pad.motion = static_cast<PadMotion>(i + 3); | ||||
|                 pad.motion_value = acc[i]; | ||||
|                 pad_queue[client].Push(pad); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Client::BeginConfiguration() { | ||||
|     for (auto& pq : pad_queue) { | ||||
|         pq.Clear(); | ||||
|     } | ||||
|     configuring = true; | ||||
| } | ||||
| 
 | ||||
| void Client::EndConfiguration() { | ||||
|     for (auto& pq : pad_queue) { | ||||
|         pq.Clear(); | ||||
|     } | ||||
|     configuring = false; | ||||
| } | ||||
| 
 | ||||
| DeviceStatus& Client::GetPadState(std::size_t pad) { | ||||
|     return clients[pad].status; | ||||
| } | ||||
| 
 | ||||
| const DeviceStatus& Client::GetPadState(std::size_t pad) const { | ||||
|     return clients[pad].status; | ||||
| } | ||||
| 
 | ||||
| std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() { | ||||
|     return pad_queue; | ||||
| } | ||||
| 
 | ||||
| const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const { | ||||
|     return pad_queue; | ||||
| } | ||||
| 
 | ||||
| void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, | ||||
|  | ||||
| @ -12,9 +12,12 @@ | ||||
| #include <thread> | ||||
| #include <tuple> | ||||
| #include "common/common_types.h" | ||||
| #include "common/param_package.h" | ||||
| #include "common/thread.h" | ||||
| #include "common/threadsafe_queue.h" | ||||
| #include "common/vector_math.h" | ||||
| #include "core/frontend/input.h" | ||||
| #include "input_common/motion_input.h" | ||||
| 
 | ||||
| namespace InputCommon::CemuhookUDP { | ||||
| 
 | ||||
| @ -29,6 +32,27 @@ struct PortInfo; | ||||
| struct Version; | ||||
| } // namespace Response
 | ||||
| 
 | ||||
| enum class PadMotion { | ||||
|     GyroX, | ||||
|     GyroY, | ||||
|     GyroZ, | ||||
|     AccX, | ||||
|     AccY, | ||||
|     AccZ, | ||||
|     Undefined, | ||||
| }; | ||||
| 
 | ||||
| enum class PadTouch { | ||||
|     Click, | ||||
|     Undefined, | ||||
| }; | ||||
| 
 | ||||
| struct UDPPadStatus { | ||||
|     PadTouch touch{PadTouch::Undefined}; | ||||
|     PadMotion motion{PadMotion::Undefined}; | ||||
|     f32 motion_value{0.0f}; | ||||
| }; | ||||
| 
 | ||||
| struct DeviceStatus { | ||||
|     std::mutex update_mutex; | ||||
|     Input::MotionStatus motion_status; | ||||
| @ -46,22 +70,58 @@ struct DeviceStatus { | ||||
| 
 | ||||
| class Client { | ||||
| public: | ||||
|     explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR, | ||||
|                     u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872); | ||||
|     // Initialize the UDP client capture and read sequence
 | ||||
|     Client(); | ||||
| 
 | ||||
|     // Close and release the client
 | ||||
|     ~Client(); | ||||
| 
 | ||||
|     // Used for polling
 | ||||
|     void BeginConfiguration(); | ||||
|     void EndConfiguration(); | ||||
| 
 | ||||
|     std::vector<Common::ParamPackage> GetInputDevices() const; | ||||
| 
 | ||||
|     bool DeviceConnected(std::size_t pad) const; | ||||
|     void ReloadUDPClient(); | ||||
|     void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0, | ||||
|                       u32 client_id = 24872); | ||||
| 
 | ||||
|     std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue(); | ||||
|     const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const; | ||||
| 
 | ||||
|     DeviceStatus& GetPadState(std::size_t pad); | ||||
|     const DeviceStatus& GetPadState(std::size_t pad) const; | ||||
| 
 | ||||
| private: | ||||
|     struct ClientData { | ||||
|         std::unique_ptr<Socket> socket; | ||||
|         DeviceStatus status; | ||||
|         std::thread thread; | ||||
|         u64 packet_sequence = 0; | ||||
|         u8 active; | ||||
| 
 | ||||
|         // Realtime values
 | ||||
|         // motion is initalized with PID values for drift correction on joycons
 | ||||
|         InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f}; | ||||
|         std::chrono::time_point<std::chrono::system_clock> last_motion_update; | ||||
|     }; | ||||
| 
 | ||||
|     // For shutting down, clear all data, join all threads, release usb
 | ||||
|     void Reset(); | ||||
| 
 | ||||
|     void OnVersion(Response::Version); | ||||
|     void OnPortInfo(Response::PortInfo); | ||||
|     void OnPadData(Response::PadData); | ||||
|     void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id); | ||||
|     void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index, | ||||
|                             u32 client_id); | ||||
|     void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc, | ||||
|                             const Common::Vec3<float>& gyro, bool touch); | ||||
| 
 | ||||
|     std::unique_ptr<Socket> socket; | ||||
|     std::shared_ptr<DeviceStatus> status; | ||||
|     std::thread thread; | ||||
|     u64 packet_sequence = 0; | ||||
|     bool configuring = false; | ||||
| 
 | ||||
|     std::array<ClientData, 4> clients; | ||||
|     std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue; | ||||
| }; | ||||
| 
 | ||||
| /// An async job allowing configuration of the touchpad calibration.
 | ||||
|  | ||||
| @ -1,105 +1,144 @@ | ||||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <list> | ||||
| #include <mutex> | ||||
| #include <optional> | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include "common/param_package.h" | ||||
| #include "core/frontend/input.h" | ||||
| #include "core/settings.h" | ||||
| #include <utility> | ||||
| #include "common/assert.h" | ||||
| #include "common/threadsafe_queue.h" | ||||
| #include "input_common/udp/client.h" | ||||
| #include "input_common/udp/udp.h" | ||||
| 
 | ||||
| namespace InputCommon::CemuhookUDP { | ||||
| namespace InputCommon { | ||||
| 
 | ||||
| class UDPTouchDevice final : public Input::TouchDevice { | ||||
| class UDPMotion final : public Input::MotionDevice { | ||||
| public: | ||||
|     explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||||
|     std::tuple<float, float, bool> GetStatus() const override { | ||||
|         std::lock_guard guard(status->update_mutex); | ||||
|         return status->touch_status; | ||||
|     } | ||||
|     UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) | ||||
|         : ip(ip_), port(port_), pad(pad_), client(client_) {} | ||||
| 
 | ||||
| private: | ||||
|     std::shared_ptr<DeviceStatus> status; | ||||
| }; | ||||
| 
 | ||||
| class UDPMotionDevice final : public Input::MotionDevice { | ||||
| public: | ||||
|     explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||||
|     Input::MotionStatus GetStatus() const override { | ||||
|         std::lock_guard guard(status->update_mutex); | ||||
|         return status->motion_status; | ||||
|         return client->GetPadState(pad).motion_status; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::shared_ptr<DeviceStatus> status; | ||||
|     const std::string ip; | ||||
|     const int port; | ||||
|     const int pad; | ||||
|     CemuhookUDP::Client* client; | ||||
|     mutable std::mutex mutex; | ||||
| }; | ||||
| 
 | ||||
| class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { | ||||
| public: | ||||
|     explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||||
| /// A motion device factory that creates motion devices from JC Adapter
 | ||||
| UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) | ||||
|     : client(std::move(client_)) {} | ||||
| 
 | ||||
|     std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override { | ||||
|         { | ||||
|             std::lock_guard guard(status->update_mutex); | ||||
|             status->touch_calibration = DeviceStatus::CalibrationData{}; | ||||
|             // These default values work well for DS4 but probably not other touch inputs
 | ||||
|             status->touch_calibration->min_x = params.Get("min_x", 100); | ||||
|             status->touch_calibration->min_y = params.Get("min_y", 50); | ||||
|             status->touch_calibration->max_x = params.Get("max_x", 1800); | ||||
|             status->touch_calibration->max_y = params.Get("max_y", 850); | ||||
| /**
 | ||||
|  * Creates motion device | ||||
|  * @param params contains parameters for creating the device: | ||||
|  *     - "port": the nth jcpad on the adapter | ||||
|  */ | ||||
| std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { | ||||
|     const std::string ip = params.Get("ip", "127.0.0.1"); | ||||
|     const int port = params.Get("port", 26760); | ||||
|     const int pad = params.Get("pad_index", 0); | ||||
| 
 | ||||
|     return std::make_unique<UDPMotion>(ip, port, pad, client.get()); | ||||
| } | ||||
| 
 | ||||
| void UDPMotionFactory::BeginConfiguration() { | ||||
|     polling = true; | ||||
|     client->BeginConfiguration(); | ||||
| } | ||||
| 
 | ||||
| void UDPMotionFactory::EndConfiguration() { | ||||
|     polling = false; | ||||
|     client->EndConfiguration(); | ||||
| } | ||||
| 
 | ||||
| Common::ParamPackage UDPMotionFactory::GetNextInput() { | ||||
|     Common::ParamPackage params; | ||||
|     CemuhookUDP::UDPPadStatus pad; | ||||
|     auto& queue = client->GetPadQueue(); | ||||
|     for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { | ||||
|         while (queue[pad_number].Pop(pad)) { | ||||
|             if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) { | ||||
|                 continue; | ||||
|             } | ||||
|             params.Set("engine", "cemuhookudp"); | ||||
|             params.Set("ip", "127.0.0.1"); | ||||
|             params.Set("port", 26760); | ||||
|             params.Set("pad_index", static_cast<int>(pad_number)); | ||||
|             params.Set("motion", static_cast<u16>(pad.motion)); | ||||
|             return params; | ||||
|         } | ||||
|         return std::make_unique<UDPTouchDevice>(status); | ||||
|     } | ||||
|     return params; | ||||
| } | ||||
| 
 | ||||
| private: | ||||
|     std::shared_ptr<DeviceStatus> status; | ||||
| }; | ||||
| 
 | ||||
| class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> { | ||||
| class UDPTouch final : public Input::TouchDevice { | ||||
| public: | ||||
|     explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||||
|     UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_) | ||||
|         : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {} | ||||
| 
 | ||||
|     std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override { | ||||
|         return std::make_unique<UDPMotionDevice>(status); | ||||
|     std::tuple<float, float, bool> GetStatus() const override { | ||||
|         return client->GetPadState(pad).touch_status; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::shared_ptr<DeviceStatus> status; | ||||
|     const std::string ip; | ||||
|     const int port; | ||||
|     const int pad; | ||||
|     CemuhookUDP::Client* client; | ||||
|     mutable std::mutex mutex; | ||||
| }; | ||||
| 
 | ||||
| State::State() { | ||||
|     auto status = std::make_shared<DeviceStatus>(); | ||||
|     client = | ||||
|         std::make_unique<Client>(status, Settings::values.udp_input_address, | ||||
|                                  Settings::values.udp_input_port, Settings::values.udp_pad_index); | ||||
| /// A motion device factory that creates motion devices from JC Adapter
 | ||||
| UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) | ||||
|     : client(std::move(client_)) {} | ||||
| 
 | ||||
|     motion_factory = std::make_shared<UDPMotionFactory>(status); | ||||
|     touch_factory = std::make_shared<UDPTouchFactory>(status); | ||||
| /**
 | ||||
|  * Creates motion device | ||||
|  * @param params contains parameters for creating the device: | ||||
|  *     - "port": the nth jcpad on the adapter | ||||
|  */ | ||||
| std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { | ||||
|     const std::string ip = params.Get("ip", "127.0.0.1"); | ||||
|     const int port = params.Get("port", 26760); | ||||
|     const int pad = params.Get("pad_index", 0); | ||||
| 
 | ||||
|     Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory); | ||||
|     Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory); | ||||
|     return std::make_unique<UDPTouch>(ip, port, pad, client.get()); | ||||
| } | ||||
| 
 | ||||
| State::~State() { | ||||
|     Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); | ||||
|     Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); | ||||
| void UDPTouchFactory::BeginConfiguration() { | ||||
|     polling = true; | ||||
|     client->BeginConfiguration(); | ||||
| } | ||||
| 
 | ||||
| std::vector<Common::ParamPackage> State::GetInputDevices() const { | ||||
|     // TODO support binding udp devices
 | ||||
|     return {}; | ||||
| void UDPTouchFactory::EndConfiguration() { | ||||
|     polling = false; | ||||
|     client->EndConfiguration(); | ||||
| } | ||||
| 
 | ||||
| void State::ReloadUDPClient() { | ||||
|     client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, | ||||
|                          Settings::values.udp_pad_index); | ||||
| Common::ParamPackage UDPTouchFactory::GetNextInput() { | ||||
|     Common::ParamPackage params; | ||||
|     CemuhookUDP::UDPPadStatus pad; | ||||
|     auto& queue = client->GetPadQueue(); | ||||
|     for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) { | ||||
|         while (queue[pad_number].Pop(pad)) { | ||||
|             if (pad.touch == CemuhookUDP::PadTouch::Undefined) { | ||||
|                 continue; | ||||
|             } | ||||
|             params.Set("engine", "cemuhookudp"); | ||||
|             params.Set("ip", "127.0.0.1"); | ||||
|             params.Set("port", 26760); | ||||
|             params.Set("pad_index", static_cast<int>(pad_number)); | ||||
|             params.Set("touch", static_cast<u16>(pad.touch)); | ||||
|             return params; | ||||
|         } | ||||
|     } | ||||
|     return params; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<State> Init() { | ||||
|     return std::make_unique<State>(); | ||||
| } | ||||
| } // namespace InputCommon::CemuhookUDP
 | ||||
| } // namespace InputCommon
 | ||||
|  | ||||
| @ -1,32 +1,57 @@ | ||||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include "common/param_package.h" | ||||
| #include "core/frontend/input.h" | ||||
| #include "input_common/udp/client.h" | ||||
| 
 | ||||
| namespace InputCommon::CemuhookUDP { | ||||
| namespace InputCommon { | ||||
| 
 | ||||
| class Client; | ||||
| class UDPMotionFactory; | ||||
| class UDPTouchFactory; | ||||
| 
 | ||||
| class State { | ||||
| /// A motion device factory that creates motion devices from udp clients
 | ||||
| class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> { | ||||
| public: | ||||
|     State(); | ||||
|     ~State(); | ||||
|     void ReloadUDPClient(); | ||||
|     std::vector<Common::ParamPackage> GetInputDevices() const; | ||||
|     explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_); | ||||
| 
 | ||||
|     std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override; | ||||
| 
 | ||||
|     Common::ParamPackage GetNextInput(); | ||||
| 
 | ||||
|     /// For device input configuration/polling
 | ||||
|     void BeginConfiguration(); | ||||
|     void EndConfiguration(); | ||||
| 
 | ||||
|     bool IsPolling() const { | ||||
|         return polling; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<Client> client; | ||||
|     std::shared_ptr<UDPMotionFactory> motion_factory; | ||||
|     std::shared_ptr<UDPTouchFactory> touch_factory; | ||||
|     std::shared_ptr<CemuhookUDP::Client> client; | ||||
|     bool polling = false; | ||||
| }; | ||||
| 
 | ||||
| std::unique_ptr<State> Init(); | ||||
| /// A touch device factory that creates touch devices from udp clients
 | ||||
| class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { | ||||
| public: | ||||
|     explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_); | ||||
| 
 | ||||
| } // namespace InputCommon::CemuhookUDP
 | ||||
|     std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; | ||||
| 
 | ||||
|     Common::ParamPackage GetNextInput(); | ||||
| 
 | ||||
|     /// For device input configuration/polling
 | ||||
|     void BeginConfiguration(); | ||||
|     void EndConfiguration(); | ||||
| 
 | ||||
|     bool IsPolling() const { | ||||
|         return polling; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::shared_ptr<CemuhookUDP::Client> client; | ||||
|     bool polling = false; | ||||
| }; | ||||
| 
 | ||||
| } // namespace InputCommon
 | ||||
|  | ||||
| @ -18,6 +18,7 @@ | ||||
| #include "core/hle/service/sm/sm.h" | ||||
| #include "input_common/gcadapter/gc_poller.h" | ||||
| #include "input_common/main.h" | ||||
| #include "input_common/udp/udp.h" | ||||
| #include "ui_configure_input_player.h" | ||||
| #include "yuzu/configuration/config.h" | ||||
| #include "yuzu/configuration/configure_input_player.h" | ||||
| @ -149,6 +150,14 @@ QString ButtonToText(const Common::ParamPackage& param) { | ||||
|         return GetKeyName(param.Get("code", 0)); | ||||
|     } | ||||
| 
 | ||||
|     if (param.Get("engine", "") == "cemuhookudp") { | ||||
|         if (param.Has("pad_index")) { | ||||
|             const QString motion_str = QString::fromStdString(param.Get("pad_index", "")); | ||||
|             return QObject::tr("Motion %1").arg(motion_str); | ||||
|         } | ||||
|         return GetKeyName(param.Get("code", 0)); | ||||
|     } | ||||
| 
 | ||||
|     if (param.Get("engine", "") == "sdl") { | ||||
|         if (param.Has("hat")) { | ||||
|             const QString hat_str = QString::fromStdString(param.Get("hat", "")); | ||||
| @ -455,6 +464,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         if (input_subsystem->GetUDPMotions()->IsPolling()) { | ||||
|             params = input_subsystem->GetUDPMotions()->GetNextInput(); | ||||
|             if (params.Has("engine")) { | ||||
|                 SetPollingResult(params, false); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         for (auto& poller : device_pollers) { | ||||
|             params = poller->GetNextInput(); | ||||
|             if (params.Has("engine")) { | ||||
| @ -746,6 +762,10 @@ void ConfigureInputPlayer::HandleClick( | ||||
|         input_subsystem->GetGCAnalogs()->BeginConfiguration(); | ||||
|     } | ||||
| 
 | ||||
|     if (type == InputCommon::Polling::DeviceType::Motion) { | ||||
|         input_subsystem->GetUDPMotions()->BeginConfiguration(); | ||||
|     } | ||||
| 
 | ||||
|     timeout_timer->start(2500); // Cancel after 2.5 seconds
 | ||||
|     poll_timer->start(50);      // Check for new inputs every 50ms
 | ||||
| } | ||||
| @ -763,6 +783,8 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, | ||||
|     input_subsystem->GetGCButtons()->EndConfiguration(); | ||||
|     input_subsystem->GetGCAnalogs()->EndConfiguration(); | ||||
| 
 | ||||
|     input_subsystem->GetUDPMotions()->EndConfiguration(); | ||||
| 
 | ||||
|     if (!abort) { | ||||
|         (*input_setter)(params); | ||||
|     } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 german
						german