diff options
Diffstat (limited to 'src/input_common')
33 files changed, 1023 insertions, 232 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index d4fa69a77..2cf9eb97f 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -1,4 +1,9 @@ +# SPDX-FileCopyrightText: 2018 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + add_library(input_common STATIC + drivers/camera.cpp + drivers/camera.h drivers/gc_adapter.cpp drivers/gc_adapter.h drivers/keyboard.cpp @@ -13,6 +18,8 @@ add_library(input_common STATIC drivers/touch_screen.h drivers/udp_client.cpp drivers/udp_client.h + drivers/virtual_amiibo.cpp + drivers/virtual_amiibo.h helpers/stick_from_buttons.cpp helpers/stick_from_buttons.h helpers/touch_from_buttons.cpp @@ -44,7 +51,6 @@ else() -Werror -Werror=conversion -Werror=ignored-qualifiers - -Werror=shadow $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> -Werror=unused-variable diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp new file mode 100644 index 000000000..dceea67e0 --- /dev/null +++ b/src/input_common/drivers/camera.cpp @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <fmt/format.h> + +#include "common/param_package.h" +#include "input_common/drivers/camera.h" + +namespace InputCommon { +constexpr PadIdentifier identifier = { + .guid = Common::UUID{}, + .port = 0, + .pad = 0, +}; + +Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) { + PreSetController(identifier); +} + +void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) { + const std::size_t desired_width = getImageWidth(); + const std::size_t desired_height = getImageHeight(); + status.data.resize(desired_width * desired_height); + + // Resize image to desired format + for (std::size_t y = 0; y < desired_height; y++) { + for (std::size_t x = 0; x < desired_width; x++) { + const std::size_t pixel_index = y * desired_width + x; + const std::size_t old_x = width * x / desired_width; + const std::size_t old_y = height * y / desired_height; + const std::size_t data_pixel_index = old_y * width + old_x; + status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF); + } + } + + SetCamera(identifier, status); +} + +std::size_t Camera::getImageWidth() const { + switch (status.format) { + case Common::Input::CameraFormat::Size320x240: + return 320; + case Common::Input::CameraFormat::Size160x120: + return 160; + case Common::Input::CameraFormat::Size80x60: + return 80; + case Common::Input::CameraFormat::Size40x30: + return 40; + case Common::Input::CameraFormat::Size20x15: + return 20; + case Common::Input::CameraFormat::None: + default: + return 0; + } +} + +std::size_t Camera::getImageHeight() const { + switch (status.format) { + case Common::Input::CameraFormat::Size320x240: + return 240; + case Common::Input::CameraFormat::Size160x120: + return 120; + case Common::Input::CameraFormat::Size80x60: + return 60; + case Common::Input::CameraFormat::Size40x30: + return 30; + case Common::Input::CameraFormat::Size20x15: + return 15; + case Common::Input::CameraFormat::None: + default: + return 0; + } +} + +Common::Input::CameraError Camera::SetCameraFormat( + [[maybe_unused]] const PadIdentifier& identifier_, + const Common::Input::CameraFormat camera_format) { + status.format = camera_format; + return Common::Input::CameraError::None; +} + +} // namespace InputCommon diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h new file mode 100644 index 000000000..b8a7c75e5 --- /dev/null +++ b/src/input_common/drivers/camera.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "input_common/input_engine.h" + +namespace InputCommon { + +/** + * A button device factory representing a keyboard. It receives keyboard events and forward them + * to all button devices it created. + */ +class Camera final : public InputEngine { +public: + explicit Camera(std::string input_engine_); + + void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data); + + std::size_t getImageWidth() const; + std::size_t getImageHeight() const; + + Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, + Common::Input::CameraFormat camera_format) override; + + Common::Input::CameraStatus status{}; +}; + +} // namespace InputCommon diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 155caae42..f4dd24e7d 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp @@ -1,6 +1,5 @@ -// Copyright 2014 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <fmt/format.h> #include <libusb.h> @@ -91,7 +90,7 @@ GCAdapter::~GCAdapter() { void GCAdapter::AdapterInputThread(std::stop_token stop_token) { LOG_DEBUG(Input, "Input thread started"); - Common::SetCurrentThreadName("yuzu:input:GCAdapter"); + Common::SetCurrentThreadName("GCAdapter"); s32 payload_size{}; AdapterPayload adapter_payload{}; @@ -215,7 +214,7 @@ void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_ } void GCAdapter::AdapterScanThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:input:ScanGCAdapter"); + Common::SetCurrentThreadName("ScanGCAdapter"); usb_adapter_handle = nullptr; pads = {}; while (!stop_token.stop_requested() && !Setup()) { @@ -524,4 +523,20 @@ Common::Input::ButtonNames GCAdapter::GetUIName(const Common::ParamPackage& para return Common::Input::ButtonNames::Invalid; } +bool GCAdapter::IsStickInverted(const Common::ParamPackage& params) { + if (!params.Has("port")) { + return false; + } + + const auto x_axis = static_cast<PadAxes>(params.Get("axis_x", 0)); + const auto y_axis = static_cast<PadAxes>(params.Get("axis_y", 0)); + if (x_axis != PadAxes::StickY && x_axis != PadAxes::SubstickY) { + return false; + } + if (y_axis != PadAxes::StickX && y_axis != PadAxes::SubstickX) { + return false; + } + return true; +} + } // namespace InputCommon diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h index 7ce1912a3..8682da847 100644 --- a/src/input_common/drivers/gc_adapter.h +++ b/src/input_common/drivers/gc_adapter.h @@ -1,12 +1,10 @@ -// Copyright 2014 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include <array> #include <memory> -#include <mutex> #include <stop_token> #include <string> #include <thread> @@ -36,6 +34,8 @@ public: AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; + bool IsStickInverted(const Common::ParamPackage& params) override; + private: enum class PadButton { Undefined = 0x0000, diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp index 59e3d9cc0..71e612fbf 100644 --- a/src/input_common/drivers/keyboard.cpp +++ b/src/input_common/drivers/keyboard.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/param_package.h" #include "common/settings_input.h" diff --git a/src/input_common/drivers/keyboard.h b/src/input_common/drivers/keyboard.h index 3856c882c..62436dba4 100644 --- a/src/input_common/drivers/keyboard.h +++ b/src/input_common/drivers/keyboard.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 3c9a4e747..98c3157a8 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <stop_token> #include <thread> @@ -38,7 +37,7 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) } void Mouse::UpdateThread(std::stop_token stop_token) { - Common::SetCurrentThreadName("yuzu:input:Mouse"); + Common::SetCurrentThreadName("Mouse"); constexpr int update_time = 10; while (!stop_token.stop_requested()) { if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index c5833b8ed..286ce1cf6 100644 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 5cf1987ad..b72e4b397 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "common/math_util.h" @@ -13,11 +12,11 @@ namespace InputCommon { namespace { -std::string GetGUID(SDL_Joystick* joystick) { +Common::UUID GetGUID(SDL_Joystick* joystick) { const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); - char guid_str[33]; - SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); - return guid_str; + std::array<u8, 16> data{}; + std::memcpy(data.data(), guid.data, sizeof(data)); + return Common::UUID{data}; } } // Anonymous namespace @@ -31,9 +30,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) { class SDLJoystick { public: - SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, + SDLJoystick(Common::UUID guid_, int port_, SDL_Joystick* joystick, SDL_GameController* game_controller) - : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, + : guid{guid_}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, sdl_controller{game_controller, &SDL_GameControllerClose} { EnableMotion(); } @@ -41,13 +40,13 @@ public: void EnableMotion() { if (sdl_controller) { SDL_GameController* controller = sdl_controller.get(); - if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) { + has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL); + has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO); + if (has_accel) { SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); - has_accel = true; } - if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) { + if (has_gyro) { SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); - has_gyro = true; } } } @@ -62,7 +61,7 @@ public: bool UpdateMotion(SDL_ControllerSensorEvent event) { constexpr float gravity_constant = 9.80665f; - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const u64 time_difference = event.timestamp - last_motion_update; last_motion_update = event.timestamp; switch (event.sensor) { @@ -120,7 +119,7 @@ public: */ const PadIdentifier GetPadIdentifier() const { return { - .guid = Common::UUID{guid}, + .guid = guid, .port = static_cast<std::size_t>(port), .pad = 0, }; @@ -129,7 +128,7 @@ public: /** * The guid of the joystick */ - const std::string& GetGUID() const { + const Common::UUID& GetGUID() const { return guid; } @@ -175,22 +174,23 @@ public: return false; } - BatteryLevel GetBatteryLevel() { + Common::Input::BatteryLevel GetBatteryLevel() { const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); switch (level) { case SDL_JOYSTICK_POWER_EMPTY: - return BatteryLevel::Empty; + return Common::Input::BatteryLevel::Empty; case SDL_JOYSTICK_POWER_LOW: - return BatteryLevel::Low; + return Common::Input::BatteryLevel::Low; case SDL_JOYSTICK_POWER_MEDIUM: - return BatteryLevel::Medium; + return Common::Input::BatteryLevel::Medium; case SDL_JOYSTICK_POWER_FULL: case SDL_JOYSTICK_POWER_MAX: - return BatteryLevel::Full; - case SDL_JOYSTICK_POWER_UNKNOWN: + return Common::Input::BatteryLevel::Full; case SDL_JOYSTICK_POWER_WIRED: + return Common::Input::BatteryLevel::Charging; + case SDL_JOYSTICK_POWER_UNKNOWN: default: - return BatteryLevel::Charging; + return Common::Input::BatteryLevel::None; } } @@ -227,7 +227,7 @@ public: } private: - std::string guid; + Common::UUID guid; int port; std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; @@ -239,8 +239,8 @@ private: BasicMotion motion; }; -std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) { - std::lock_guard lock{joystick_map_mutex}; +std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const Common::UUID& guid, int port) { + std::scoped_lock lock{joystick_map_mutex}; const auto it = joystick_map.find(guid); if (it != joystick_map.end()) { @@ -258,11 +258,15 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& return joystick_map[guid].emplace_back(std::move(joystick)); } +std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) { + return GetSDLJoystickByGUID(Common::UUID{guid}, port); +} + std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); - const std::string guid = GetGUID(sdl_joystick); + const auto guid = GetGUID(sdl_joystick); - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; const auto map_it = joystick_map.find(guid); if (map_it == joystick_map.end()) { @@ -294,13 +298,14 @@ void SDLDriver::InitJoystick(int joystick_index) { return; } - const std::string guid = GetGUID(sdl_joystick); + const auto guid = GetGUID(sdl_joystick); - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); + joystick->EnableMotion(); joystick_map[guid].emplace_back(std::move(joystick)); return; } @@ -312,6 +317,7 @@ void SDLDriver::InitJoystick(int joystick_index) { if (joystick_it != joystick_guid_list.end()) { (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller); + (*joystick_it)->EnableMotion(); return; } @@ -319,13 +325,14 @@ void SDLDriver::InitJoystick(int joystick_index) { auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel()); + joystick->EnableMotion(); joystick_guid_list.emplace_back(std::move(joystick)); } void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) { - const std::string guid = GetGUID(sdl_joystick); + const auto guid = GetGUID(sdl_joystick); - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; // This call to guid is safe since the joystick is guaranteed to be in the map const auto& joystick_guid_list = joystick_map[guid]; const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), @@ -351,6 +358,8 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { const PadIdentifier identifier = joystick->GetPadIdentifier(); SetButton(identifier, event.jbutton.button, true); + // Battery doesn't trigger an event so just update every button press + SetBattery(identifier, joystick->GetBatteryLevel()); } break; } @@ -389,7 +398,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { } void SDLDriver::CloseJoysticks() { - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; joystick_map.clear(); } @@ -427,13 +436,21 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en initialized = true; if (start_thread) { poll_thread = std::thread([this] { - Common::SetCurrentThreadName("yuzu:input:SDL"); + Common::SetCurrentThreadName("SDL_MainLoop"); using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); std::this_thread::sleep_for(1ms); } }); + vibration_thread = std::thread([this] { + Common::SetCurrentThreadName("SDL_Vibration"); + using namespace std::chrono_literals; + while (initialized) { + SendVibrations(); + std::this_thread::sleep_for(10ms); + } + }); } // Because the events for joystick connection happens before we have our event watcher added, we // can just open all the joysticks right here @@ -449,6 +466,7 @@ SDLDriver::~SDLDriver() { initialized = false; if (start_thread) { poll_thread.join(); + vibration_thread.join(); SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); } } @@ -466,7 +484,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { devices.emplace_back(Common::ParamPackage{ {"engine", GetEngineName()}, {"display", std::move(name)}, - {"guid", joystick->GetGUID()}, + {"guid", joystick->GetGUID().RawString()}, {"port", std::to_string(joystick->GetPort())}, }); if (joystick->IsJoyconLeft()) { @@ -489,8 +507,8 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const { devices.emplace_back(Common::ParamPackage{ {"engine", GetEngineName()}, {"display", std::move(name)}, - {"guid", joystick->GetGUID()}, - {"guid2", joystick2->GetGUID()}, + {"guid", joystick->GetGUID().RawString()}, + {"guid2", joystick2->GetGUID().RawString()}, {"port", std::to_string(joystick->GetPort())}, }); } @@ -528,57 +546,75 @@ Common::Input::VibrationError SDLDriver::SetRumble( .type = Common::Input::VibrationAmplificationType::Exponential, }; - if (!joystick->RumblePlay(new_vibration)) { - return Common::Input::VibrationError::Unknown; + if (vibration.type == Common::Input::VibrationAmplificationType::Test) { + if (!joystick->RumblePlay(new_vibration)) { + return Common::Input::VibrationError::Unknown; + } + return Common::Input::VibrationError::None; } + vibration_queue.Push(VibrationRequest{ + .identifier = identifier, + .vibration = new_vibration, + }); + return Common::Input::VibrationError::None; } -Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid, +void SDLDriver::SendVibrations() { + while (!vibration_queue.Empty()) { + VibrationRequest request; + vibration_queue.Pop(request); + const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(), + static_cast<int>(request.identifier.port)); + joystick->RumblePlay(request.vibration); + } +} + +Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, const Common::UUID& guid, s32 axis, float value) const { Common::ParamPackage params{}; params.Set("engine", GetEngineName()); params.Set("port", port); - params.Set("guid", std::move(guid)); + params.Set("guid", guid.RawString()); params.Set("axis", axis); params.Set("threshold", "0.5"); params.Set("invert", value < 0 ? "-" : "+"); return params; } -Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, std::string guid, +Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, const Common::UUID& guid, s32 button) const { Common::ParamPackage params{}; params.Set("engine", GetEngineName()); params.Set("port", port); - params.Set("guid", std::move(guid)); + params.Set("guid", guid.RawString()); params.Set("button", button); return params; } -Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, std::string guid, s32 hat, - u8 value) const { +Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, const Common::UUID& guid, + s32 hat, u8 value) const { Common::ParamPackage params{}; params.Set("engine", GetEngineName()); params.Set("port", port); - params.Set("guid", std::move(guid)); + params.Set("guid", guid.RawString()); params.Set("hat", hat); params.Set("direction", GetHatButtonName(value)); return params; } -Common::ParamPackage SDLDriver::BuildMotionParam(int port, std::string guid) const { +Common::ParamPackage SDLDriver::BuildMotionParam(int port, const Common::UUID& guid) const { Common::ParamPackage params{}; params.Set("engine", GetEngineName()); params.Set("motion", 0); params.Set("port", port); - params.Set("guid", std::move(guid)); + params.Set("guid", guid.RawString()); return params; } Common::ParamPackage SDLDriver::BuildParamPackageForBinding( - int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const { + int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const { switch (binding.bindType) { case SDL_CONTROLLER_BINDTYPE_NONE: break; @@ -931,4 +967,37 @@ u8 SDLDriver::GetHatButtonId(const std::string& direction_name) const { return direction; } +bool SDLDriver::IsStickInverted(const Common::ParamPackage& params) { + if (!params.Has("guid") || !params.Has("port")) { + return false; + } + const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); + if (joystick == nullptr) { + return false; + } + auto* controller = joystick->GetSDLGameController(); + if (controller == nullptr) { + return false; + } + + const auto& axis_x = params.Get("axis_x", 0); + const auto& axis_y = params.Get("axis_y", 0); + const auto& binding_left_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); + const auto& binding_right_x = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); + const auto& binding_left_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); + const auto& binding_right_y = + SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); + + if (axis_x != binding_left_y.value.axis && axis_x != binding_right_y.value.axis) { + return false; + } + if (axis_y != binding_left_x.value.axis && axis_y != binding_right_x.value.axis) { + return false; + } + return true; +} + } // namespace InputCommon diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index 4cde3606f..fc3a44572 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -1,6 +1,5 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -12,6 +11,7 @@ #include <SDL.h> #include "common/common_types.h" +#include "common/threadsafe_queue.h" #include "input_common/input_engine.h" union SDL_Event; @@ -46,6 +46,7 @@ public: * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so * tie it to a SDLJoystick with the same guid and that port */ + std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const Common::UUID& guid, int port); std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); std::vector<Common::ParamPackage> GetInputDevices() const override; @@ -58,28 +59,38 @@ public: std::string GetHatButtonName(u8 direction_value) const override; u8 GetHatButtonId(const std::string& direction_name) const override; + bool IsStickInverted(const Common::ParamPackage& params) override; + Common::Input::VibrationError SetRumble( const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; private: + struct VibrationRequest { + PadIdentifier identifier; + Common::Input::VibrationStatus vibration; + }; + void InitJoystick(int joystick_index); void CloseJoystick(SDL_Joystick* sdl_joystick); /// Needs to be called before SDL_QuitSubSystem. void CloseJoysticks(); - Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, - float value = 0.1f) const; - Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, + /// Takes all vibrations from the queue and sends the command to the controller + void SendVibrations(); + + Common::ParamPackage BuildAnalogParamPackageForButton(int port, const Common::UUID& guid, + s32 axis, float value = 0.1f) const; + Common::ParamPackage BuildButtonParamPackageForButton(int port, const Common::UUID& guid, s32 button) const; - Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, + Common::ParamPackage BuildHatParamPackageForButton(int port, const Common::UUID& guid, s32 hat, u8 value) const; - Common::ParamPackage BuildMotionParam(int port, std::string guid) const; + Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const; Common::ParamPackage BuildParamPackageForBinding( - int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const; + int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const; Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, int axis_y, float offset_x, @@ -105,13 +116,17 @@ private: /// Returns true if the button is on the left joycon bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; + /// Queue of vibration request to controllers + Common::SPSCQueue<VibrationRequest> vibration_queue; + /// Map of GUID of a list of corresponding virtual Joysticks - std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; + std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; std::mutex joystick_map_mutex; bool start_thread = false; std::atomic<bool> initialized = false; std::thread poll_thread; + std::thread vibration_thread; }; } // namespace InputCommon diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp index 944e141bf..21c6ed405 100644 --- a/src/input_common/drivers/tas_input.cpp +++ b/src/input_common/drivers/tas_input.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstring> #include <fmt/format.h> diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h index 4b4e6c417..38a27a230 100644 --- a/src/input_common/drivers/tas_input.h +++ b/src/input_common/drivers/tas_input.h @@ -1,6 +1,5 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp index 30c727df4..1753e0893 100644 --- a/src/input_common/drivers/touch_screen.cpp +++ b/src/input_common/drivers/touch_screen.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/param_package.h" #include "input_common/drivers/touch_screen.h" @@ -15,38 +14,93 @@ constexpr PadIdentifier identifier = { TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) { PreSetController(identifier); + ReleaseAllTouch(); } -void TouchScreen::TouchMoved(float x, float y, std::size_t finger) { - if (finger >= 16) { +void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) { + const auto index = GetIndexFromFingerId(finger_id); + if (!index) { + // Touch doesn't exist handle it as a new one + TouchPressed(x, y, finger_id); return; } - TouchPressed(x, y, finger); + const auto i = index.value(); + fingers[i].is_active = true; + SetButton(identifier, static_cast<int>(i), true); + SetAxis(identifier, static_cast<int>(i * 2), x); + SetAxis(identifier, static_cast<int>(i * 2 + 1), y); } -void TouchScreen::TouchPressed(float x, float y, std::size_t finger) { - if (finger >= 16) { +void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) { + if (GetIndexFromFingerId(finger_id)) { + // Touch already exist. Just update the data + TouchMoved(x, y, finger_id); return; } - SetButton(identifier, static_cast<int>(finger), true); - SetAxis(identifier, static_cast<int>(finger * 2), x); - SetAxis(identifier, static_cast<int>(finger * 2 + 1), y); + const auto index = GetNextFreeIndex(); + if (!index) { + // No free entries. Ignore input + return; + } + const auto i = index.value(); + fingers[i].is_enabled = true; + fingers[i].finger_id = finger_id; + TouchMoved(x, y, finger_id); } -void TouchScreen::TouchReleased(std::size_t finger) { - if (finger >= 16) { +void TouchScreen::TouchReleased(std::size_t finger_id) { + const auto index = GetIndexFromFingerId(finger_id); + if (!index) { return; } - SetButton(identifier, static_cast<int>(finger), false); - SetAxis(identifier, static_cast<int>(finger * 2), 0.0f); - SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f); + const auto i = index.value(); + fingers[i].is_enabled = false; + SetButton(identifier, static_cast<int>(i), false); + SetAxis(identifier, static_cast<int>(i * 2), 0.0f); + SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f); +} + +std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const { + for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) { + const auto& finger = fingers[index]; + if (!finger.is_enabled) { + continue; + } + if (finger.finger_id == finger_id) { + return index; + } + } + return std::nullopt; +} + +std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const { + for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) { + if (!fingers[index].is_enabled) { + return index; + } + } + return std::nullopt; +} + +void TouchScreen::ClearActiveFlag() { + for (auto& finger : fingers) { + finger.is_active = false; + } +} + +void TouchScreen::ReleaseInactiveTouch() { + for (const auto& finger : fingers) { + if (!finger.is_active) { + TouchReleased(finger.finger_id); + } + } } void TouchScreen::ReleaseAllTouch() { - for (int index = 0; index < 16; ++index) { - SetButton(identifier, index, false); - SetAxis(identifier, index * 2, 0.0f); - SetAxis(identifier, index * 2 + 1, 0.0f); + for (const auto& finger : fingers) { + if (finger.is_enabled) { + TouchReleased(finger.finger_id); + } } } diff --git a/src/input_common/drivers/touch_screen.h b/src/input_common/drivers/touch_screen.h index bf395c40b..f46036ffd 100644 --- a/src/input_common/drivers/touch_screen.h +++ b/src/input_common/drivers/touch_screen.h @@ -1,44 +1,67 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include <optional> + #include "input_common/input_engine.h" namespace InputCommon { /** - * A button device factory representing a keyboard. It receives keyboard events and forward them - * to all button devices it created. + * A touch device factory representing a touch screen. It receives touch events and forward them + * to all touch devices it created. */ class TouchScreen final : public InputEngine { public: explicit TouchScreen(std::string input_engine_); /** - * Signals that mouse has moved. - * @param x the x-coordinate of the cursor - * @param y the y-coordinate of the cursor - * @param center_x the x-coordinate of the middle of the screen - * @param center_y the y-coordinate of the middle of the screen + * Signals that touch has moved and marks this touch point as active + * @param x new horizontal position + * @param y new vertical position + * @param finger_id of the touch point to be updated */ - void TouchMoved(float x, float y, std::size_t finger); + void TouchMoved(float x, float y, std::size_t finger_id); /** - * Sets the status of all buttons bound with the key to pressed - * @param key_code the code of the key to press + * Signals and creates a new touch point with this finger id + * @param x starting horizontal position + * @param y starting vertical position + * @param finger_id to be assigned to the new touch point */ - void TouchPressed(float x, float y, std::size_t finger); + void TouchPressed(float x, float y, std::size_t finger_id); /** - * Sets the status of all buttons bound with the key to released - * @param key_code the code of the key to release + * Signals and resets the touch point related to the this finger id + * @param finger_id to be released */ - void TouchReleased(std::size_t finger); + void TouchReleased(std::size_t finger_id); + + /// Resets the active flag for each touch point + void ClearActiveFlag(); + + /// Releases all touch that haven't been marked as active + void ReleaseInactiveTouch(); /// Resets all inputs to their initial value void ReleaseAllTouch(); + +private: + static constexpr std::size_t MAX_FINGER_COUNT = 16; + + struct TouchStatus { + std::size_t finger_id{}; + bool is_enabled{}; + bool is_active{}; + }; + + std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const; + + std::optional<std::size_t> GetNextFreeIndex() const; + + std::array<TouchStatus, MAX_FINGER_COUNT> fingers{}; }; } // namespace InputCommon diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 64162f431..808b21069 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <random> #include <boost/asio.hpp> @@ -192,22 +191,22 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const { return MAX_UDP_CLIENTS; } -BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const { +Common::Input::BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const { switch (battery) { case Response::Battery::Dying: - return BatteryLevel::Empty; + return Common::Input::BatteryLevel::Empty; case Response::Battery::Low: - return BatteryLevel::Critical; + return Common::Input::BatteryLevel::Critical; case Response::Battery::Medium: - return BatteryLevel::Low; + return Common::Input::BatteryLevel::Low; case Response::Battery::High: - return BatteryLevel::Medium; + return Common::Input::BatteryLevel::Medium; case Response::Battery::Full: case Response::Battery::Charged: - return BatteryLevel::Full; + return Common::Input::BatteryLevel::Full; case Response::Battery::Charging: default: - return BatteryLevel::Charging; + return Common::Input::BatteryLevel::Charging; } } @@ -547,6 +546,22 @@ Common::Input::ButtonNames UDPClient::GetUIName(const Common::ParamPackage& para return Common::Input::ButtonNames::Invalid; } +bool UDPClient::IsStickInverted(const Common::ParamPackage& params) { + if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) { + return false; + } + + const auto x_axis = static_cast<PadAxes>(params.Get("axis_x", 0)); + const auto y_axis = static_cast<PadAxes>(params.Get("axis_y", 0)); + if (x_axis != PadAxes::LeftStickY && x_axis != PadAxes::RightStickY) { + return false; + } + if (y_axis != PadAxes::LeftStickX && y_axis != PadAxes::RightStickX) { + return false; + } + return true; +} + void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) { diff --git a/src/input_common/drivers/udp_client.h b/src/input_common/drivers/udp_client.h index 76e32bd04..cea9f579a 100644 --- a/src/input_common/drivers/udp_client.h +++ b/src/input_common/drivers/udp_client.h @@ -1,6 +1,5 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -64,6 +63,8 @@ public: MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; + bool IsStickInverted(const Common::ParamPackage& params) override; + private: enum class PadButton { Undefined = 0x0000, @@ -141,7 +142,7 @@ private: std::size_t GetClientNumber(std::string_view host, u16 port) const; // Translates UDP battery level to input engine battery level - BatteryLevel GetBatteryLevel(Response::Battery battery) const; + Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const; void OnVersion(Response::Version); void OnPortInfo(Response::PortInfo); diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp new file mode 100644 index 000000000..0cd5129da --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <cstring> +#include <fmt/format.h> + +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "input_common/drivers/virtual_amiibo.h" + +namespace InputCommon { +constexpr PadIdentifier identifier = { + .guid = Common::UUID{}, + .port = 0, + .pad = 0, +}; + +VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} + +VirtualAmiibo::~VirtualAmiibo() = default; + +Common::Input::PollingError VirtualAmiibo::SetPollingMode( + [[maybe_unused]] const PadIdentifier& identifier_, + const Common::Input::PollingMode polling_mode_) { + polling_mode = polling_mode_; + + if (polling_mode == Common::Input::PollingMode::NFC) { + if (state == State::Initialized) { + state = State::WaitingForAmiibo; + } + } else { + if (state == State::AmiiboIsOpen) { + CloseAmiibo(); + } + } + + return Common::Input::PollingError::None; +} + +Common::Input::NfcState VirtualAmiibo::SupportsNfc( + [[maybe_unused]] const PadIdentifier& identifier_) const { + return Common::Input::NfcState::Success; +} + +Common::Input::NfcState VirtualAmiibo::WriteNfcData( + [[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) { + const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, + Common::FS::FileType::BinaryFile}; + + if (!amiibo_file.IsOpen()) { + LOG_ERROR(Core, "Amiibo is already on use"); + return Common::Input::NfcState::WriteFailed; + } + + if (!amiibo_file.Write(data)) { + LOG_ERROR(Service_NFP, "Error writting to file"); + return Common::Input::NfcState::WriteFailed; + } + + return Common::Input::NfcState::Success; +} + +VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const { + return state; +} + +VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { + const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + if (state != State::WaitingForAmiibo) { + return Info::WrongDeviceState; + } + + if (!amiibo_file.IsOpen()) { + return Info::UnableToLoad; + } + + amiibo_data.resize(amiibo_size); + + if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) { + return Info::NotAnAmiibo; + } + + file_path = filename; + state = State::AmiiboIsOpen; + SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); + return Info::Success; +} + +VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { + state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo + : State::Initialized; + SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}}); + return Info::Success; +} + +} // namespace InputCommon diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h new file mode 100644 index 000000000..9eac07544 --- /dev/null +++ b/src/input_common/drivers/virtual_amiibo.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> +#include <string> +#include <vector> + +#include "common/common_types.h" +#include "input_common/input_engine.h" + +namespace Common::FS { +class IOFile; +} + +namespace InputCommon { + +class VirtualAmiibo final : public InputEngine { +public: + enum class State { + Initialized, + WaitingForAmiibo, + AmiiboIsOpen, + }; + + enum class Info { + Success, + UnableToLoad, + NotAnAmiibo, + WrongDeviceState, + Unknown, + }; + + explicit VirtualAmiibo(std::string input_engine_); + ~VirtualAmiibo() override; + + // Sets polling mode to a controller + Common::Input::PollingError SetPollingMode( + const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; + + Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; + + Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_, + const std::vector<u8>& data) override; + + State GetCurrentState() const; + + Info LoadAmiibo(const std::string& amiibo_file); + Info CloseAmiibo(); + +private: + static constexpr std::size_t amiibo_size = 0x21C; + static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; + + std::string file_path{}; + State state{State::Initialized}; + std::vector<u8> amiibo_data; + Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive}; +}; +} // namespace InputCommon diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp index 31e6f62ab..536d413a5 100644 --- a/src/input_common/helpers/stick_from_buttons.cpp +++ b/src/input_common/helpers/stick_from_buttons.cpp @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <chrono> #include <cmath> diff --git a/src/input_common/helpers/stick_from_buttons.h b/src/input_common/helpers/stick_from_buttons.h index 437ace4f7..e8d865743 100644 --- a/src/input_common/helpers/stick_from_buttons.h +++ b/src/input_common/helpers/stick_from_buttons.h @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp index f1b57d03a..da4a3dca5 100644 --- a/src/input_common/helpers/touch_from_buttons.cpp +++ b/src/input_common/helpers/touch_from_buttons.cpp @@ -1,6 +1,5 @@ -// Copyright 2020 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2020 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <algorithm> #include "common/settings.h" diff --git a/src/input_common/helpers/touch_from_buttons.h b/src/input_common/helpers/touch_from_buttons.h index 628f18215..c6cb3ab3c 100644 --- a/src/input_common/helpers/touch_from_buttons.h +++ b/src/input_common/helpers/touch_from_buttons.h @@ -1,6 +1,5 @@ -// Copyright 2020 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2020 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/input_common/helpers/udp_protocol.cpp b/src/input_common/helpers/udp_protocol.cpp index cdeab7e11..994380d21 100644 --- a/src/input_common/helpers/udp_protocol.cpp +++ b/src/input_common/helpers/udp_protocol.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <cstddef> #include <cstring> diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h index 2d5d54ddb..d9643ffe0 100644 --- a/src/input_common/helpers/udp_protocol.h +++ b/src/input_common/helpers/udp_protocol.h @@ -1,6 +1,5 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2018 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -8,9 +7,17 @@ #include <optional> #include <type_traits> +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used +#endif + #include <boost/crc.hpp> -#include "common/bit_field.h" +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #include "common/swap.h" namespace InputCommon::CemuhookUDP { @@ -77,7 +84,7 @@ enum RegisterFlags : u8 { struct Version {}; /** * Requests the server to send information about what controllers are plugged into the ports - * In citra's case, we only have one controller, so for simplicity's sake, we can just send a + * In yuzu's case, we only have one controller, so for simplicity's sake, we can just send a * request explicitly for the first controller port and leave it at that. In the future it would be * nice to make this configurable */ diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 65ae1b848..61cfd0911 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp @@ -1,45 +1,43 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "common/param_package.h" #include "input_common/input_engine.h" namespace InputCommon { void InputEngine::PreSetController(const PadIdentifier& identifier) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; controller_list.try_emplace(identifier); } void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.buttons.try_emplace(button, false); } void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.hat_buttons.try_emplace(button, u8{0}); } void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.axes.try_emplace(axis, 0.0f); } void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.motions.try_emplace(motion); } void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.buttons.insert_or_assign(button, value); @@ -50,7 +48,7 @@ void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool va void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.hat_buttons.insert_or_assign(button, value); @@ -61,7 +59,7 @@ void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 v void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.axes.insert_or_assign(axis, value); @@ -70,9 +68,9 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) TriggerOnAxisChange(identifier, axis, value); } -void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) { +void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.battery = value; @@ -83,7 +81,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.motions.insert_or_assign(motion, value); @@ -92,8 +90,31 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B TriggerOnMotionChange(identifier, motion, value); } +void InputEngine::SetCamera(const PadIdentifier& identifier, + const Common::Input::CameraStatus& value) { + { + std::scoped_lock lock{mutex}; + ControllerData& controller = controller_list.at(identifier); + if (!configuring) { + controller.camera = value; + } + } + TriggerOnCameraChange(identifier, value); +} + +void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) { + { + std::scoped_lock lock{mutex}; + ControllerData& controller = controller_list.at(identifier); + if (!configuring) { + controller.nfc = value; + } + } + TriggerOnNfcChange(identifier, value); +} + bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -110,7 +131,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { } bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -127,7 +148,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d } f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -143,20 +164,20 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { return axis_iter->second; } -BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { - std::lock_guard lock{mutex}; +Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), identifier.pad, identifier.port); - return BatteryLevel::Charging; + return Common::Input::BatteryLevel::Charging; } const ControllerData& controller = controller_iter->second; return controller.battery; } BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -167,6 +188,30 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) return controller.motions.at(motion); } +Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const { + std::scoped_lock lock{mutex}; + const auto controller_iter = controller_list.find(identifier); + if (controller_iter == controller_list.cend()) { + LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), + identifier.pad, identifier.port); + return {}; + } + const ControllerData& controller = controller_iter->second; + return controller.camera; +} + +Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const { + std::scoped_lock lock{mutex}; + const auto controller_iter = controller_list.find(identifier); + if (controller_iter == controller_list.cend()) { + LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), + identifier.pad, identifier.port); + return {}; + } + const ControllerData& controller = controller_iter->second; + return controller.nfc; +} + void InputEngine::ResetButtonState() { for (const auto& controller : controller_list) { for (const auto& button : controller.second.buttons) { @@ -187,7 +232,7 @@ void InputEngine::ResetAnalogState() { } void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) { @@ -215,7 +260,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but } void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) { @@ -244,7 +289,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int } void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) { @@ -270,8 +315,8 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, } void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, - [[maybe_unused]] BatteryLevel value) { - std::lock_guard lock{mutex_callback}; + [[maybe_unused]] Common::Input::BatteryLevel value) { + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) { @@ -285,7 +330,7 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, const BasicMotion& value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) { @@ -319,6 +364,34 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot }); } +void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier, + [[maybe_unused]] const Common::Input::CameraStatus& value) { + std::scoped_lock lock{mutex_callback}; + for (const auto& poller_pair : callback_list) { + const InputIdentifier& poller = poller_pair.second; + if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) { + continue; + } + if (poller.callback.on_change) { + poller.callback.on_change(); + } + } +} + +void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier, + [[maybe_unused]] const Common::Input::NfcStatus& value) { + std::scoped_lock lock{mutex_callback}; + for (const auto& poller_pair : callback_list) { + const InputIdentifier& poller = poller_pair.second; + if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) { + continue; + } + if (poller.callback.on_change) { + poller.callback.on_change(); + } + } +} + bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, const PadIdentifier& identifier, EngineInputType type, int index) const { @@ -347,18 +420,18 @@ const std::string& InputEngine::GetEngineName() const { } int InputEngine::SetCallback(InputIdentifier input_identifier) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; callback_list.insert_or_assign(last_callback_key, std::move(input_identifier)); return last_callback_key++; } void InputEngine::SetMappingCallback(MappingCallback callback) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; mapping_callback = std::move(callback); } void InputEngine::DeleteCallback(int key) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index c6c027aef..cfbdb26bd 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -34,24 +33,16 @@ struct BasicMotion { u64 delta_timestamp{}; }; -// Stages of a battery charge -enum class BatteryLevel { - Empty, - Critical, - Low, - Medium, - Full, - Charging, -}; - // Types of input that are stored in the engine enum class EngineInputType { None, + Analog, + Battery, Button, + Camera, HatButton, - Analog, Motion, - Battery, + Nfc, }; namespace std { @@ -126,10 +117,29 @@ public: // Sets polling mode to a controller virtual Common::Input::PollingError SetPollingMode( [[maybe_unused]] const PadIdentifier& identifier, - [[maybe_unused]] const Common::Input::PollingMode vibration) { + [[maybe_unused]] const Common::Input::PollingMode polling_mode) { return Common::Input::PollingError::NotSupported; } + // Sets camera format to a controller + virtual Common::Input::CameraError SetCameraFormat( + [[maybe_unused]] const PadIdentifier& identifier, + [[maybe_unused]] Common::Input::CameraFormat camera_format) { + return Common::Input::CameraError::NotSupported; + } + + // Request nfc data from a controller + virtual Common::Input::NfcState SupportsNfc( + [[maybe_unused]] const PadIdentifier& identifier) const { + return Common::Input::NfcState::NotSupported; + } + + // Writes data to an nfc tag + virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier, + [[maybe_unused]] const std::vector<u8>& data) { + return Common::Input::NfcState::NotSupported; + } + // Returns the engine name [[nodiscard]] const std::string& GetEngineName() const; @@ -167,6 +177,11 @@ public: return 0; } + /// Returns true if axis of a stick aren't mapped in the correct direction + virtual bool IsStickInverted([[maybe_unused]] const Common::ParamPackage& params) { + return false; + } + void PreSetController(const PadIdentifier& identifier); void PreSetButton(const PadIdentifier& identifier, int button); void PreSetHatButton(const PadIdentifier& identifier, int button); @@ -178,8 +193,10 @@ public: bool GetButton(const PadIdentifier& identifier, int button) const; bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; f32 GetAxis(const PadIdentifier& identifier, int axis) const; - BatteryLevel GetBattery(const PadIdentifier& identifier) const; + Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; + Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; + Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; int SetCallback(InputIdentifier input_identifier); void SetMappingCallback(MappingCallback callback); @@ -189,8 +206,10 @@ protected: void SetButton(const PadIdentifier& identifier, int button, bool value); void SetHatButton(const PadIdentifier& identifier, int button, u8 value); void SetAxis(const PadIdentifier& identifier, int axis, f32 value); - void SetBattery(const PadIdentifier& identifier, BatteryLevel value); + void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); + void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); + void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { return "Unknown"; @@ -202,15 +221,20 @@ private: std::unordered_map<int, u8> hat_buttons; std::unordered_map<int, float> axes; std::unordered_map<int, BasicMotion> motions; - BatteryLevel battery{}; + Common::Input::BatteryLevel battery{}; + Common::Input::CameraStatus camera{}; + Common::Input::NfcStatus nfc{}; }; void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); - void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value); + void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, const BasicMotion& value); + void TriggerOnCameraChange(const PadIdentifier& identifier, + const Common::Input::CameraStatus& value); + void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, const PadIdentifier& identifier, EngineInputType type, diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp index fb78093b8..0fa4b1ddb 100644 --- a/src/input_common/input_mapping.cpp +++ b/src/input_common/input_mapping.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" #include "input_common/input_engine.h" diff --git a/src/input_common/input_mapping.h b/src/input_common/input_mapping.h index e0dfbc7ad..79bd083e0 100644 --- a/src/input_common/input_mapping.h +++ b/src/input_common/input_mapping.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 7f3c08597..75705b67e 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/common_types.h" #include "common/input.h" @@ -470,7 +469,7 @@ public: } Common::Input::BatteryStatus GetStatus() const { - return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier)); + return input_engine->GetBattery(identifier); } void ForceUpdate() override { @@ -665,6 +664,88 @@ private: InputEngine* input_engine; }; +class InputFromCamera final : public Common::Input::InputDevice { +public: + explicit InputFromCamera(PadIdentifier identifier_, InputEngine* input_engine_) + : identifier(identifier_), input_engine(input_engine_) { + UpdateCallback engine_callback{[this]() { OnChange(); }}; + const InputIdentifier input_identifier{ + .identifier = identifier, + .type = EngineInputType::Camera, + .index = 0, + .callback = engine_callback, + }; + callback_key = input_engine->SetCallback(input_identifier); + } + + ~InputFromCamera() override { + input_engine->DeleteCallback(callback_key); + } + + Common::Input::CameraStatus GetStatus() const { + return input_engine->GetCamera(identifier); + } + + void ForceUpdate() override { + OnChange(); + } + + void OnChange() { + const Common::Input::CallbackStatus status{ + .type = Common::Input::InputType::IrSensor, + .camera_status = GetStatus(), + }; + + TriggerOnChange(status); + } + +private: + const PadIdentifier identifier; + int callback_key; + InputEngine* input_engine; +}; + +class InputFromNfc final : public Common::Input::InputDevice { +public: + explicit InputFromNfc(PadIdentifier identifier_, InputEngine* input_engine_) + : identifier(identifier_), input_engine(input_engine_) { + UpdateCallback engine_callback{[this]() { OnChange(); }}; + const InputIdentifier input_identifier{ + .identifier = identifier, + .type = EngineInputType::Nfc, + .index = 0, + .callback = engine_callback, + }; + callback_key = input_engine->SetCallback(input_identifier); + } + + ~InputFromNfc() override { + input_engine->DeleteCallback(callback_key); + } + + Common::Input::NfcStatus GetStatus() const { + return input_engine->GetNfc(identifier); + } + + void ForceUpdate() override { + OnChange(); + } + + void OnChange() { + const Common::Input::CallbackStatus status{ + .type = Common::Input::InputType::Nfc, + .nfc_status = GetStatus(), + }; + + TriggerOnChange(status); + } + +private: + const PadIdentifier identifier; + int callback_key; + InputEngine* input_engine; +}; + class OutputFromIdentifier final : public Common::Input::OutputDevice { public: explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) @@ -683,6 +764,18 @@ public: return input_engine->SetPollingMode(identifier, polling_mode); } + Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override { + return input_engine->SetCameraFormat(identifier, camera_format); + } + + Common::Input::NfcState SupportsNfc() const override { + return input_engine->SupportsNfc(identifier); + } + + Common::Input::NfcState WriteNfcData(const std::vector<u8>& data) override { + return input_engine->WriteNfcData(identifier, data); + } + private: const PadIdentifier identifier; InputEngine* input_engine; @@ -733,7 +826,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice( std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( const Common::ParamPackage& params) { const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f); - const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); + const auto range = std::clamp(params.Get("range", 0.95f), 0.25f, 1.50f); const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); const PadIdentifier identifier = { .guid = Common::UUID{params.Get("guid", "")}, @@ -780,6 +873,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice( .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f), .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f), .inverted = params.Get("invert", "+") == "-", + .toggle = static_cast<bool>(params.Get("toggle", false)), }; input_engine->PreSetController(identifier); input_engine->PreSetAxis(identifier, axis); @@ -921,6 +1015,30 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( properties_y, properties_z, input_engine.get()); } +std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice( + const Common::ParamPackage& params) { + const PadIdentifier identifier = { + .guid = Common::UUID{params.Get("guid", "")}, + .port = static_cast<std::size_t>(params.Get("port", 0)), + .pad = static_cast<std::size_t>(params.Get("pad", 0)), + }; + + input_engine->PreSetController(identifier); + return std::make_unique<InputFromCamera>(identifier, input_engine.get()); +} + +std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateNfcDevice( + const Common::ParamPackage& params) { + const PadIdentifier identifier = { + .guid = Common::UUID{params.Get("guid", "")}, + .port = static_cast<std::size_t>(params.Get("port", 0)), + .pad = static_cast<std::size_t>(params.Get("pad", 0)), + }; + + input_engine->PreSetController(identifier); + return std::make_unique<InputFromNfc>(identifier, input_engine.get()); +} + InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_) : input_engine(std::move(input_engine_)) {} @@ -929,6 +1047,12 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create( if (params.Has("battery")) { return CreateBatteryDevice(params); } + if (params.Has("camera")) { + return CreateCameraDevice(params); + } + if (params.Has("nfc")) { + return CreateNfcDevice(params); + } if (params.Has("button") && params.Has("axis")) { return CreateTriggerDevice(params); } diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h index 8a0977d58..d7db13ce4 100644 --- a/src/input_common/input_poller.h +++ b/src/input_common/input_poller.h @@ -1,6 +1,5 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -212,6 +211,27 @@ private: */ std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params); + /** + * Creates a camera device from the parameters given. + * @param params contains parameters for creating the device: + * - "guid": text string for identifying controllers + * - "port": port of the connected device + * - "pad": slot of the connected controller + * @returns a unique input device with the parameters specified + */ + std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice( + const Common::ParamPackage& params); + + /** + * Creates a nfc device from the parameters given. + * @param params contains parameters for creating the device: + * - "guid": text string for identifying controllers + * - "port": port of the connected device + * - "pad": slot of the connected controller + * @returns a unique input device with the parameters specified + */ + std::unique_ptr<Common::Input::InputDevice> CreateNfcDevice(const Common::ParamPackage& params); + std::shared_ptr<InputEngine> input_engine; }; } // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index a4d7ed645..b2064ef95 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -1,17 +1,17 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include <memory> -#include <thread> #include "common/input.h" #include "common/param_package.h" +#include "input_common/drivers/camera.h" #include "input_common/drivers/gc_adapter.h" #include "input_common/drivers/keyboard.h" #include "input_common/drivers/mouse.h" #include "input_common/drivers/tas_input.h" #include "input_common/drivers/touch_screen.h" #include "input_common/drivers/udp_client.h" +#include "input_common/drivers/virtual_amiibo.h" #include "input_common/helpers/stick_from_buttons.h" #include "input_common/helpers/touch_from_buttons.h" #include "input_common/input_engine.h" @@ -79,6 +79,24 @@ struct InputSubsystem::Impl { Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(), tas_output_factory); + camera = std::make_shared<Camera>("camera"); + camera->SetMappingCallback(mapping_callback); + camera_input_factory = std::make_shared<InputFactory>(camera); + camera_output_factory = std::make_shared<OutputFactory>(camera); + Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(), + camera_input_factory); + Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(), + camera_output_factory); + + virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo"); + virtual_amiibo->SetMappingCallback(mapping_callback); + virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo); + virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo); + Common::Input::RegisterFactory<Common::Input::InputDevice>(virtual_amiibo->GetEngineName(), + virtual_amiibo_input_factory); + Common::Input::RegisterFactory<Common::Input::OutputDevice>(virtual_amiibo->GetEngineName(), + virtual_amiibo_output_factory); + #ifdef HAVE_SDL2 sdl = std::make_shared<SDLDriver>("sdl"); sdl->SetMappingCallback(mapping_callback); @@ -242,6 +260,28 @@ struct InputSubsystem::Impl { return Common::Input::ButtonNames::Invalid; } + bool IsStickInverted(const Common::ParamPackage& params) { + const std::string engine = params.Get("engine", ""); + if (engine == mouse->GetEngineName()) { + return mouse->IsStickInverted(params); + } + if (engine == gcadapter->GetEngineName()) { + return gcadapter->IsStickInverted(params); + } + if (engine == udp_client->GetEngineName()) { + return udp_client->IsStickInverted(params); + } + if (engine == tas_input->GetEngineName()) { + return tas_input->IsStickInverted(params); + } +#ifdef HAVE_SDL2 + if (engine == sdl->GetEngineName()) { + return sdl->IsStickInverted(params); + } +#endif + return false; + } + bool IsController(const Common::ParamPackage& params) { const std::string engine = params.Get("engine", ""); if (engine == mouse->GetEngineName()) { @@ -296,6 +336,8 @@ struct InputSubsystem::Impl { std::shared_ptr<TouchScreen> touch_screen; std::shared_ptr<TasInput::Tas> tas_input; std::shared_ptr<CemuhookUDP::UDPClient> udp_client; + std::shared_ptr<Camera> camera; + std::shared_ptr<VirtualAmiibo> virtual_amiibo; std::shared_ptr<InputFactory> keyboard_factory; std::shared_ptr<InputFactory> mouse_factory; @@ -303,12 +345,16 @@ struct InputSubsystem::Impl { std::shared_ptr<InputFactory> touch_screen_factory; std::shared_ptr<InputFactory> udp_client_input_factory; std::shared_ptr<InputFactory> tas_input_factory; + std::shared_ptr<InputFactory> camera_input_factory; + std::shared_ptr<InputFactory> virtual_amiibo_input_factory; std::shared_ptr<OutputFactory> keyboard_output_factory; std::shared_ptr<OutputFactory> mouse_output_factory; std::shared_ptr<OutputFactory> gcadapter_output_factory; std::shared_ptr<OutputFactory> udp_client_output_factory; std::shared_ptr<OutputFactory> tas_output_factory; + std::shared_ptr<OutputFactory> camera_output_factory; + std::shared_ptr<OutputFactory> virtual_amiibo_output_factory; #ifdef HAVE_SDL2 std::shared_ptr<SDLDriver> sdl; @@ -361,6 +407,22 @@ const TasInput::Tas* InputSubsystem::GetTas() const { return impl->tas_input.get(); } +Camera* InputSubsystem::GetCamera() { + return impl->camera.get(); +} + +const Camera* InputSubsystem::GetCamera() const { + return impl->camera.get(); +} + +VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { + return impl->virtual_amiibo.get(); +} + +const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const { + return impl->virtual_amiibo.get(); +} + std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { return impl->GetInputDevices(); } @@ -385,6 +447,13 @@ bool InputSubsystem::IsController(const Common::ParamPackage& params) const { return impl->IsController(params); } +bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const { + if (params.Has("axis_x") && params.Has("axis_y")) { + return impl->IsStickInverted(params); + } + return false; +} + void InputSubsystem::ReloadInputDevices() { impl->udp_client.get()->ReloadSockets(); } diff --git a/src/input_common/main.h b/src/input_common/main.h index baf107e0f..ced252383 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -1,6 +1,5 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,9 +29,11 @@ enum Values : int; } namespace InputCommon { +class Camera; class Keyboard; class Mouse; class TouchScreen; +class VirtualAmiibo; struct MappingData; } // namespace InputCommon @@ -92,9 +93,21 @@ public: /// Retrieves the underlying tas input device. [[nodiscard]] TasInput::Tas* GetTas(); - /// Retrieves the underlying tas input device. + /// Retrieves the underlying tas input device. [[nodiscard]] const TasInput::Tas* GetTas() const; + /// Retrieves the underlying camera input device. + [[nodiscard]] Camera* GetCamera(); + + /// Retrieves the underlying camera input device. + [[nodiscard]] const Camera* GetCamera() const; + + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] VirtualAmiibo* GetVirtualAmiibo(); + + /// Retrieves the underlying virtual amiibo input device. + [[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const; + /** * Returns all available input devices that this Factory can create a new device with. * Each returned ParamPackage should have a `display` field used for display, a `engine` field @@ -119,6 +132,9 @@ public: /// Returns true if device is a controller. [[nodiscard]] bool IsController(const Common::ParamPackage& params) const; + /// Returns true if axis of a stick aren't mapped in the correct direction + [[nodiscard]] bool IsStickInverted(const Common::ParamPackage& device) const; + /// Reloads the input devices. void ReloadInputDevices(); |