summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/frontend/input.h7
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp48
-rw-r--r--src/core/hle/service/hid/controllers/npad.h11
-rw-r--r--src/core/hle/service/hid/hid.cpp1
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp6
-rw-r--r--src/input_common/gcadapter/gc_adapter.h4
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp50
-rw-r--r--src/input_common/gcadapter/gc_poller.h11
-rw-r--r--src/input_common/main.cpp5
-rw-r--r--src/input_common/sdl/sdl_impl.cpp74
-rw-r--r--src/input_common/sdl/sdl_impl.h2
-rw-r--r--src/input_common/settings.cpp21
-rw-r--r--src/input_common/settings.h32
-rw-r--r--src/yuzu/applets/controller.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp61
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp7
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp80
-rw-r--r--src/yuzu/configuration/configure_vibration.h3
-rw-r--r--src/yuzu/main.cpp3
19 files changed, 327 insertions, 101 deletions
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index fb2ce2514..25ac5af46 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -122,6 +122,13 @@ using ButtonDevice = InputDevice<bool>;
using AnalogDevice = InputDevice<std::tuple<float, float>>;
/**
+ * A vibration device is an input device that returns an unsigned byte as status.
+ * It represents whether the vibration device supports vibration or not.
+ * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
+ */
+using VibrationDevice = InputDevice<u8>;
+
+/**
* A motion status is an object that returns a tuple of accelerometer state vector,
* gyroscope state vector, rotation state vector and orientation state matrix.
*
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index dc9954377..27099de24 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -271,6 +271,10 @@ void Controller_NPad::OnLoadInputDevices() {
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
+ std::transform(players[i].vibrations.begin() +
+ Settings::NativeVibration::VIBRATION_HID_BEGIN,
+ players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
+ vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
@@ -278,8 +282,10 @@ void Controller_NPad::OnLoadInputDevices() {
}
void Controller_NPad::OnRelease() {
- for (std::size_t index = 0; index < connected_controllers.size(); ++index) {
- VibrateControllerAtIndex(index, {});
+ for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
+ for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
+ VibrateControllerAtIndex(npad_idx, device_idx);
+ }
}
}
@@ -674,9 +680,9 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode)
}
}
-bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index,
+bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
const VibrationValue& vibration_value) {
- if (!connected_controllers[npad_index].is_connected) {
+ if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
return false;
}
@@ -686,10 +692,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index,
return false;
}
- using namespace Settings::NativeButton;
- const auto& button_state = buttons[npad_index];
-
- return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
+ return vibrations[npad_index][device_index]->SetRumblePlay(
std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f),
vibration_value.freq_low,
std::min(vibration_value.amp_high * player.vibration_strength / 100.0f, 1.0f),
@@ -717,6 +720,11 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
continue;
}
+ if (vibration_device_handles[i].device_index == DeviceIndex::None) {
+ UNREACHABLE_MSG("DeviceIndex should never be None!");
+ continue;
+ }
+
// Some games try to send mismatched parameters in the device handle, block these.
if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
(vibration_device_handles[i].npad_type == NpadType::JoyconRight ||
@@ -747,28 +755,8 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
continue;
}
- // TODO: Vibrate left/right vibration motors independently if possible.
- if (VibrateControllerAtIndex(npad_index, vibration_values[i])) {
- switch (connected_controllers[npad_index].type) {
- case NPadControllerType::None:
- UNREACHABLE();
- break;
- case NPadControllerType::ProController:
- case NPadControllerType::Handheld:
- case NPadControllerType::JoyDual:
- // Since we can't vibrate motors independently yet, we can reduce state changes by
- // assigning all 3 device indices the current vibration value.
- latest_vibration_values[npad_index][0] = vibration_values[i];
- latest_vibration_values[npad_index][1] = vibration_values[i];
- latest_vibration_values[npad_index][2] = vibration_values[i];
- break;
- case NPadControllerType::JoyLeft:
- case NPadControllerType::JoyRight:
- case NPadControllerType::Pokeball:
- default:
- latest_vibration_values[npad_index][device_index] = vibration_values[i];
- break;
- }
+ if (VibrateControllerAtIndex(npad_index, device_index, vibration_values[i])) {
+ latest_vibration_values[npad_index][device_index] = vibration_values[i];
}
}
}
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 576ef1558..3ae9fb8e6 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -148,7 +148,8 @@ public:
void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
- bool VibrateControllerAtIndex(std::size_t npad_index, const VibrationValue& vibration_value);
+ bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
+ const VibrationValue& vibration_value = {});
void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
const std::vector<VibrationValue>& vibration_values);
@@ -399,18 +400,22 @@ private:
using StickArray = std::array<
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
10>;
+ using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
+ Settings::NativeVibration::NUM_VIBRATIONS_HID>,
+ 10>;
using MotionArray = std::array<
- std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
+ std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
10>;
ButtonArray buttons;
StickArray sticks;
+ VibrationArray vibrations;
MotionArray motions;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
// Each controller should have their own styleset changed event
std::array<Kernel::EventPair, 10> styleset_changed_events;
- std::array<std::array<VibrationValue, 3>, 10> latest_vibration_values;
+ std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
std::array<ControllerHolder, 10> connected_controllers{};
std::array<bool, 10> unintended_home_button_input_protection{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index e88f30d6a..1d882a977 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -998,6 +998,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
break;
case Controller_NPad::DeviceIndex::None:
default:
+ UNREACHABLE_MSG("DeviceIndex should never be None!");
vibration_device_info.position = VibrationDevicePosition::None;
break;
}
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index b912188b6..d80195c82 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -230,10 +230,8 @@ void Adapter::SendVibrations() {
vibration_changed = false;
}
-bool Adapter::RumblePlay(std::size_t port, f32 amplitude) {
- amplitude = std::clamp(amplitude, 0.0f, 1.0f);
- const auto raw_amp = static_cast<u8>(amplitude * 0x8);
- pads[port].rumble_amplitude = raw_amp;
+bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
+ pads[port].rumble_amplitude = amplitude;
return rumble_enabled;
}
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index d28dcfad3..f1256c9da 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -77,8 +77,8 @@ public:
Adapter();
~Adapter();
- /// Request a vibration for a controlelr
- bool RumblePlay(std::size_t port, f32 amplitude);
+ /// Request a vibration for a controller
+ bool RumblePlay(std::size_t port, u8 amplitude);
/// Used for polling
void BeginConfiguration();
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 6bd6f57fc..fe57c13a5 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,7 +15,7 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice {
public:
- explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
+ explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
: port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override;
@@ -27,18 +27,10 @@ public:
return false;
}
- bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
- const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
- const auto new_amp =
- static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
-
- return gcadapter->RumblePlay(port, new_amp);
- }
-
private:
const u32 port;
const s32 button;
- GCAdapter::Adapter* gcadapter;
+ const GCAdapter::Adapter* gcadapter;
};
class GCAxisButton final : public Input::ButtonDevice {
@@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
return params;
}
+class GCVibration final : public Input::VibrationDevice {
+public:
+ explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
+ : port(port_), gcadapter(adapter) {}
+
+ u8 GetStatus() const override {
+ return gcadapter->RumblePlay(port, 0);
+ }
+
+ bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
+ const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
+ const auto processed_amplitude = static_cast<u8>(
+ pow(mean_amplitude, 0.5f) * (3.0f - 2.0f * pow(mean_amplitude, 0.15f)) * 0x8);
+
+ return gcadapter->RumblePlay(port, processed_amplitude);
+ }
+
+private:
+ const u32 port;
+ GCAdapter::Adapter* gcadapter;
+};
+
+/// An vibration device factory that creates vibration devices from GC Adapter
+GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
+ : adapter(std::move(adapter_)) {}
+
+/**
+ * Creates a vibration device from a joystick
+ * @param params contains parameters for creating the device:
+ * - "port": the nth gcpad on the adapter
+ */
+std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
+ const Common::ParamPackage& params) {
+ const auto port = static_cast<u32>(params.Get("port", 0));
+
+ return std::make_unique<GCVibration>(port, adapter.get());
+}
+
} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
index 0527f328f..d1271e3ea 100644
--- a/src/input_common/gcadapter/gc_poller.h
+++ b/src/input_common/gcadapter/gc_poller.h
@@ -64,4 +64,15 @@ private:
bool polling = false;
};
+/// A vibration device factory creates vibration devices from GC Adapter
+class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
+public:
+ explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
+
+ std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
+
+private:
+ std::shared_ptr<GCAdapter::Adapter> adapter;
+};
+
} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b438482cc..e59ad4ff5 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -28,6 +28,8 @@ struct InputSubsystem::Impl {
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
+ gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
+ Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
keyboard = std::make_shared<Keyboard>();
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@@ -64,9 +66,11 @@ struct InputSubsystem::Impl {
#endif
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
+ Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
gcbuttons.reset();
gcanalog.reset();
+ gcvibration.reset();
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
@@ -142,6 +146,7 @@ struct InputSubsystem::Impl {
#endif
std::shared_ptr<GCButtonFactory> gcbuttons;
std::shared_ptr<GCAnalogFactory> gcanalog;
+ std::shared_ptr<GCVibrationFactory> gcvibration;
std::shared_ptr<UDPMotionFactory> udpmotion;
std::shared_ptr<UDPTouchFactory> udptouch;
std::shared_ptr<CemuhookUDP::Client> udp;
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 18fb2ac5e..a2a83cdc9 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -85,16 +85,17 @@ public:
using std::chrono::milliseconds;
using std::chrono::steady_clock;
- // Prevent vibrations less than 10ms apart from each other.
- if (duration_cast<milliseconds>(steady_clock::now() - last_vibration) < milliseconds(10)) {
+ // Block non-zero vibrations less than 10ms apart from each other.
+ if ((amp_low != 0 || amp_high != 0) &&
+ duration_cast<milliseconds>(steady_clock::now() - last_vibration) < milliseconds(10)) {
return false;
- };
+ }
last_vibration = steady_clock::now();
- if (sdl_controller != nullptr) {
+ if (sdl_controller) {
return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
- } else if (sdl_joystick != nullptr) {
+ } else if (sdl_joystick) {
return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
}
@@ -321,14 +322,6 @@ public:
return joystick->GetButton(button);
}
- bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
- const u16 processed_amp_low =
- static_cast<u16>(pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)) * 0xFFFF);
- const u16 processed_amp_high =
- static_cast<u16>(pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)) * 0xFFFF);
- return joystick->RumblePlay(processed_amp_low, processed_amp_high);
- }
-
private:
std::shared_ptr<SDLJoystick> joystick;
int button;
@@ -412,6 +405,32 @@ private:
const float range;
};
+class SDLVibration final : public Input::VibrationDevice {
+public:
+ explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
+ : joystick(std::move(joystick_)) {}
+
+ u8 GetStatus() const override {
+ joystick->RumblePlay(1, 1);
+ return joystick->RumblePlay(0, 0);
+ }
+
+ bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
+ const auto process_amplitude = [](f32 amplitude) {
+ return static_cast<u16>(std::pow(amplitude, 0.5f) *
+ (3.0f - 2.0f * std::pow(amplitude, 0.15f)) * 0xFFFF);
+ };
+
+ const auto processed_amp_low = process_amplitude(amp_low);
+ const auto processed_amp_high = process_amplitude(amp_high);
+
+ return joystick->RumblePlay(processed_amp_low, processed_amp_high);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+};
+
class SDLDirectionMotion final : public Input::MotionDevice {
public:
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@@ -554,7 +573,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
/**
- * Creates analog device from joystick axes
+ * Creates an analog device from joystick axes
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
@@ -580,6 +599,26 @@ private:
SDLState& state;
};
+/// An vibration device factory that creates vibration devices from SDL joystick
+class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
+public:
+ explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
+ /**
+ * Creates a vibration device from a joystick
+ * @param params contains parameters for creating the device:
+ * - "guid": the guid of the joystick to bind
+ * - "port": the nth joystick of the same type
+ */
+ std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
+ const std::string guid = params.Get("guid", "0");
+ const int port = params.Get("port", 0);
+ return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
+ }
+
+private:
+ SDLState& state;
+};
+
/// A motion device factory that creates motion devices from SDL joystick
class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
public:
@@ -646,11 +685,13 @@ private:
SDLState::SDLState() {
using namespace Input;
- analog_factory = std::make_shared<SDLAnalogFactory>(*this);
button_factory = std::make_shared<SDLButtonFactory>(*this);
+ analog_factory = std::make_shared<SDLAnalogFactory>(*this);
+ vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
motion_factory = std::make_shared<SDLMotionFactory>(*this);
- RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<ButtonDevice>("sdl", button_factory);
+ RegisterFactory<AnalogDevice>("sdl", analog_factory);
+ RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
// If the frontend is going to manage the event loop, then we don't start one here
@@ -687,6 +728,7 @@ SDLState::~SDLState() {
using namespace Input;
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
+ UnregisterFactory<VibrationDevice>("sdl");
UnregisterFactory<MotionDevice>("sdl");
CloseJoysticks();
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index b9bb4dc56..08044b00d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -22,6 +22,7 @@ namespace InputCommon::SDL {
class SDLAnalogFactory;
class SDLButtonFactory;
class SDLMotionFactory;
+class SDLVibrationFactory;
class SDLJoystick;
class SDLState : public State {
@@ -72,6 +73,7 @@ private:
std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory;
+ std::shared_ptr<SDLVibrationFactory> vibration_factory;
std::shared_ptr<SDLMotionFactory> motion_factory;
bool start_thread = false;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
index b66c05856..557e7a9a0 100644
--- a/src/input_common/settings.cpp
+++ b/src/input_common/settings.cpp
@@ -14,13 +14,6 @@ const std::array<const char*, NumButtons> mapping = {{
}};
}
-namespace NativeMotion {
-const std::array<const char*, NumMotions> mapping = {{
- "motionleft",
- "motionright",
-}};
-}
-
namespace NativeAnalog {
const std::array<const char*, NumAnalogs> mapping = {{
"lstick",
@@ -28,6 +21,20 @@ const std::array<const char*, NumAnalogs> mapping = {{
}};
}
+namespace NativeVibration {
+const std::array<const char*, NumVibrations> mapping = {{
+ "left_vibration_device",
+ "right_vibration_device",
+}};
+}
+
+namespace NativeMotion {
+const std::array<const char*, NumMotions> mapping = {{
+ "motionleft",
+ "motionright",
+}};
+}
+
namespace NativeMouseButton {
const std::array<const char*, NumMouseButtons> mapping = {{
"left",
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
index 2763ed991..75486554b 100644
--- a/src/input_common/settings.h
+++ b/src/input_common/settings.h
@@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog
+namespace NativeVibration {
+enum Values : int {
+ LeftVibrationDevice,
+ RightVibrationDevice,
+
+ NumVibrations,
+};
+
+constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice;
+constexpr int VIBRATION_HID_END = NumVibrations;
+constexpr int NUM_VIBRATIONS_HID = NumVibrations;
+
+extern const std::array<const char*, NumVibrations> mapping;
+}; // namespace NativeVibration
+
namespace NativeMotion {
enum Values : int {
- MOTIONLEFT,
- MOTIONRIGHT,
+ MotionLeft,
+ MotionRight,
NumMotions,
};
-constexpr int MOTION_HID_BEGIN = MOTIONLEFT;
+constexpr int MOTION_HID_BEGIN = MotionLeft;
constexpr int MOTION_HID_END = NumMotions;
-constexpr int NUM_MOTION_HID = NumMotions;
+constexpr int NUM_MOTIONS_HID = NumMotions;
extern const std::array<const char*, NumMotions> mapping;
} // namespace NativeMotion
@@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
} // namespace NativeKeyboard
-using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
-using MotionRaw = std::array<std::string, NativeMotion::NumMotions>;
+using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
+using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
+using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
+
using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -330,7 +347,8 @@ struct PlayerInput {
ControllerType controller_type;
ButtonsRaw buttons;
AnalogsRaw analogs;
- MotionRaw motions;
+ VibrationsRaw vibrations;
+ MotionsRaw motions;
bool vibration_enabled;
int vibration_strength;
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index c5e671309..cdcd3d28d 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -478,6 +478,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
return;
}
+ ConfigureVibration::SetVibrationDevices(player_index);
+
// Player 1 and Handheld
auto& handheld = Settings::values.players.GetValue()[8];
// If Handheld is selected, copy all the settings from Player 1 to Handheld.
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7f66f29aa..6fa842cd5 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -344,21 +344,6 @@ void Config::ReadPlayerValue(std::size_t player_index) {
}
}
- for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
- const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
- auto& player_motions = player.motions[i];
-
- player_motions = qt_config
- ->value(QStringLiteral("%1").arg(player_prefix) +
- QString::fromUtf8(Settings::NativeMotion::mapping[i]),
- QString::fromStdString(default_param))
- .toString()
- .toStdString();
- if (player_motions.empty()) {
- player_motions = default_param;
- }
- }
-
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -375,6 +360,33 @@ void Config::ReadPlayerValue(std::size_t player_index) {
player_analogs = default_param;
}
}
+
+ for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
+ auto& player_vibrations = player.vibrations[i];
+
+ player_vibrations =
+ qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeVibration::mapping[i]),
+ QString{})
+ .toString()
+ .toStdString();
+ }
+
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
+ auto& player_motions = player.motions[i];
+
+ player_motions = qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeMotion::mapping[i]),
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (player_motions.empty()) {
+ player_motions = default_param;
+ }
+ }
}
void Config::ReadDebugValues() {
@@ -1014,13 +1026,6 @@ void Config::SavePlayerValue(std::size_t player_index) {
QString::fromStdString(player.buttons[i]),
QString::fromStdString(default_param));
}
- for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
- const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
- WriteSetting(QStringLiteral("%1").arg(player_prefix) +
- QString::fromStdString(Settings::NativeMotion::mapping[i]),
- QString::fromStdString(player.motions[i]),
- QString::fromStdString(default_param));
- }
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -1030,6 +1035,18 @@ void Config::SavePlayerValue(std::size_t player_index) {
QString::fromStdString(player.analogs[i]),
QString::fromStdString(default_param));
}
+ for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeVibration::mapping[i]),
+ QString::fromStdString(player.vibrations[i]), QString{});
+ }
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeMotion::mapping[i]),
+ QString::fromStdString(player.motions[i]),
+ QString::fromStdString(default_param));
+ }
}
void Config::SaveDebugValues() {
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 460ff08a4..3e785c224 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -22,6 +22,7 @@
#include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h"
+#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
#include "yuzu/util/limitable_input_dialog.h"
@@ -39,6 +40,10 @@ namespace {
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
bool connected) {
+ auto& player = Settings::values.players.GetValue()[npad_index];
+ player.controller_type = controller_type;
+ player.connected = connected;
+
Core::System& system{Core::System::GetInstance()};
if (!system.IsPoweredOn()) {
return;
@@ -565,6 +570,8 @@ void ConfigureInputPlayer::ApplyConfiguration() {
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
player.connected = ui->groupConnectedController->isChecked();
+ ConfigureVibration::SetVibrationDevices(player_index);
+
// Player 2-8
if (player_index != 0) {
UpdateController(player.controller_type, player_index, player.connected);
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index 1c68f28f3..714db5b80 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -2,6 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
+#include <unordered_map>
+
+#include <fmt/format.h>
+
+#include "common/param_package.h"
#include "core/settings.h"
#include "ui_configure_vibration.h"
#include "yuzu/configuration/configure_vibration.h"
@@ -53,6 +59,80 @@ void ConfigureVibration::ApplyConfiguration() {
ui->checkBoxAccurateVibration->isChecked());
}
+void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
+ using namespace Settings::NativeButton;
+ static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
+ {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
+ {A, B, X, Y, R, ZR}, // Right Buttons
+ }};
+
+ auto& player = Settings::values.players.GetValue()[player_index];
+
+ for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
+ std::unordered_map<std::string, int> params_count;
+
+ for (const auto button_index : buttons[device_idx]) {
+ const auto& player_button = player.buttons[button_index];
+
+ if (params_count.find(player_button) != params_count.end()) {
+ ++params_count[player_button];
+ continue;
+ }
+
+ params_count.insert_or_assign(player_button, 1);
+ }
+
+ const auto it = std::max_element(
+ params_count.begin(), params_count.end(),
+ [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
+
+ auto& vibration_param_str = player.vibrations[device_idx];
+ vibration_param_str.clear();
+
+ if (it->first.empty()) {
+ continue;
+ }
+
+ const auto param = Common::ParamPackage(it->first);
+
+ const auto engine = param.Get("engine", "");
+ const auto guid = param.Get("guid", "");
+ const auto port = param.Get("port", "");
+
+ if (engine.empty() || engine == "keyboard") {
+ continue;
+ }
+
+ vibration_param_str += fmt::format("engine:{}", engine);
+
+ if (!port.empty()) {
+ vibration_param_str += fmt::format(",port:{}", port);
+ }
+ if (!guid.empty()) {
+ vibration_param_str += fmt::format(",guid:{}", guid);
+ }
+ }
+
+ if (player.vibrations[0] != player.vibrations[1]) {
+ return;
+ }
+
+ if (!player.vibrations[0].empty() &&
+ player.controller_type != Settings::ControllerType::RightJoycon) {
+ player.vibrations[1].clear();
+ } else if (!player.vibrations[1].empty() &&
+ player.controller_type == Settings::ControllerType::RightJoycon) {
+ player.vibrations[0].clear();
+ }
+}
+
+void ConfigureVibration::SetAllVibrationDevices() {
+ // Set vibration devices for all player indices including handheld
+ for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
+ SetVibrationDevices(player_idx);
+ }
+}
+
void ConfigureVibration::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h
index 37bbc2653..07411a86f 100644
--- a/src/yuzu/configuration/configure_vibration.h
+++ b/src/yuzu/configuration/configure_vibration.h
@@ -24,6 +24,9 @@ public:
void ApplyConfiguration();
+ static void SetVibrationDevices(std::size_t player_index);
+ static void SetAllVibrationDevices();
+
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 54a46827f..76a5c32f4 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -18,6 +18,7 @@
#include "applets/web_browser.h"
#include "configuration/configure_input.h"
#include "configuration/configure_per_game.h"
+#include "configuration/configure_vibration.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/controller.h"
@@ -1096,6 +1097,8 @@ void GMainWindow::BootGame(const QString& filename) {
Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
}
+ ConfigureVibration::SetAllVibrationDevices();
+
Settings::LogSettings();
if (UISettings::values.select_user_on_boot) {