summaryrefslogtreecommitdiff
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/CMakeLists.txt8
-rw-r--r--src/input_common/drivers/camera.cpp82
-rw-r--r--src/input_common/drivers/camera.h29
-rw-r--r--src/input_common/drivers/gc_adapter.cpp25
-rw-r--r--src/input_common/drivers/gc_adapter.h8
-rw-r--r--src/input_common/drivers/keyboard.cpp5
-rw-r--r--src/input_common/drivers/keyboard.h5
-rw-r--r--src/input_common/drivers/mouse.cpp7
-rw-r--r--src/input_common/drivers/mouse.h5
-rw-r--r--src/input_common/drivers/sdl_driver.cpp167
-rw-r--r--src/input_common/drivers/sdl_driver.h35
-rw-r--r--src/input_common/drivers/tas_input.cpp5
-rw-r--r--src/input_common/drivers/tas_input.h5
-rw-r--r--src/input_common/drivers/touch_screen.cpp94
-rw-r--r--src/input_common/drivers/touch_screen.h57
-rw-r--r--src/input_common/drivers/udp_client.cpp35
-rw-r--r--src/input_common/drivers/udp_client.h9
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp101
-rw-r--r--src/input_common/drivers/virtual_amiibo.h61
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp5
-rw-r--r--src/input_common/helpers/stick_from_buttons.h5
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp5
-rw-r--r--src/input_common/helpers/touch_from_buttons.h5
-rw-r--r--src/input_common/helpers/udp_protocol.cpp5
-rw-r--r--src/input_common/helpers/udp_protocol.h17
-rw-r--r--src/input_common/input_engine.cpp135
-rw-r--r--src/input_common/input_engine.h64
-rw-r--r--src/input_common/input_mapping.cpp5
-rw-r--r--src/input_common/input_mapping.h5
-rw-r--r--src/input_common/input_poller.cpp134
-rw-r--r--src/input_common/input_poller.h26
-rw-r--r--src/input_common/main.cpp77
-rw-r--r--src/input_common/main.h24
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();