summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Morph2020-10-20 13:55:25 -0400
committerGravatar Morph2020-11-15 23:33:20 -0500
commite9e1876e821b8bd1bb5c8254ec93e2cc479e16dd (patch)
tree344b40a5a874cb188c6e7aa7d1622c77a215090d /src
parentconfigure_input: Add per-player vibration (diff)
downloadyuzu-e9e1876e821b8bd1bb5c8254ec93e2cc479e16dd.tar.gz
yuzu-e9e1876e821b8bd1bb5c8254ec93e2cc479e16dd.tar.xz
yuzu-e9e1876e821b8bd1bb5c8254ec93e2cc479e16dd.zip
input_common: Add VibrationDevice and VibrationDeviceFactory
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.
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>;
122using AnalogDevice = InputDevice<std::tuple<float, float>>; 122using AnalogDevice = InputDevice<std::tuple<float, float>>;
123 123
124/** 124/**
125 * A vibration device is an input device that returns an unsigned byte as status.
126 * It represents whether the vibration device supports vibration or not.
127 * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
128 */
129using VibrationDevice = InputDevice<u8>;
130
131/**
125 * A motion status is an object that returns a tuple of accelerometer state vector, 132 * A motion status is an object that returns a tuple of accelerometer state vector,
126 * gyroscope state vector, rotation state vector and orientation state matrix. 133 * gyroscope state vector, rotation state vector and orientation state matrix.
127 * 134 *
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() {
271 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 271 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
272 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 272 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
273 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); 273 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
274 std::transform(players[i].vibrations.begin() +
275 Settings::NativeVibration::VIBRATION_HID_BEGIN,
276 players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
277 vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
274 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, 278 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
275 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, 279 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
276 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>); 280 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
@@ -278,8 +282,10 @@ void Controller_NPad::OnLoadInputDevices() {
278} 282}
279 283
280void Controller_NPad::OnRelease() { 284void Controller_NPad::OnRelease() {
281 for (std::size_t index = 0; index < connected_controllers.size(); ++index) { 285 for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
282 VibrateControllerAtIndex(index, {}); 286 for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
287 VibrateControllerAtIndex(npad_idx, device_idx);
288 }
283 } 289 }
284} 290}
285 291
@@ -674,9 +680,9 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode)
674 } 680 }
675} 681}
676 682
677bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, 683bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
678 const VibrationValue& vibration_value) { 684 const VibrationValue& vibration_value) {
679 if (!connected_controllers[npad_index].is_connected) { 685 if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
680 return false; 686 return false;
681 } 687 }
682 688
@@ -686,10 +692,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index,
686 return false; 692 return false;
687 } 693 }
688 694
689 using namespace Settings::NativeButton; 695 return vibrations[npad_index][device_index]->SetRumblePlay(
690 const auto& button_state = buttons[npad_index];
691
692 return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
693 std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), 696 std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f),
694 vibration_value.freq_low, 697 vibration_value.freq_low,
695 std::min(vibration_value.amp_high * player.vibration_strength / 100.0f, 1.0f), 698 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
717 continue; 720 continue;
718 } 721 }
719 722
723 if (vibration_device_handles[i].device_index == DeviceIndex::None) {
724 UNREACHABLE_MSG("DeviceIndex should never be None!");
725 continue;
726 }
727
720 // Some games try to send mismatched parameters in the device handle, block these. 728 // Some games try to send mismatched parameters in the device handle, block these.
721 if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && 729 if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
722 (vibration_device_handles[i].npad_type == NpadType::JoyconRight || 730 (vibration_device_handles[i].npad_type == NpadType::JoyconRight ||
@@ -747,28 +755,8 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
747 continue; 755 continue;
748 } 756 }
749 757
750 // TODO: Vibrate left/right vibration motors independently if possible. 758 if (VibrateControllerAtIndex(npad_index, device_index, vibration_values[i])) {
751 if (VibrateControllerAtIndex(npad_index, vibration_values[i])) { 759 latest_vibration_values[npad_index][device_index] = vibration_values[i];
752 switch (connected_controllers[npad_index].type) {
753 case NPadControllerType::None:
754 UNREACHABLE();
755 break;
756 case NPadControllerType::ProController:
757 case NPadControllerType::Handheld:
758 case NPadControllerType::JoyDual:
759 // Since we can't vibrate motors independently yet, we can reduce state changes by
760 // assigning all 3 device indices the current vibration value.
761 latest_vibration_values[npad_index][0] = vibration_values[i];
762 latest_vibration_values[npad_index][1] = vibration_values[i];
763 latest_vibration_values[npad_index][2] = vibration_values[i];
764 break;
765 case NPadControllerType::JoyLeft:
766 case NPadControllerType::JoyRight:
767 case NPadControllerType::Pokeball:
768 default:
769 latest_vibration_values[npad_index][device_index] = vibration_values[i];
770 break;
771 }
772 } 760 }
773 } 761 }
774} 762}
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:
148 148
149 void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); 149 void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
150 150
151 bool VibrateControllerAtIndex(std::size_t npad_index, const VibrationValue& vibration_value); 151 bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
152 const VibrationValue& vibration_value = {});
152 153
153 void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, 154 void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
154 const std::vector<VibrationValue>& vibration_values); 155 const std::vector<VibrationValue>& vibration_values);
@@ -399,18 +400,22 @@ private:
399 using StickArray = std::array< 400 using StickArray = std::array<
400 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 401 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
401 10>; 402 10>;
403 using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
404 Settings::NativeVibration::NUM_VIBRATIONS_HID>,
405 10>;
402 using MotionArray = std::array< 406 using MotionArray = std::array<
403 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>, 407 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
404 10>; 408 10>;
405 ButtonArray buttons; 409 ButtonArray buttons;
406 StickArray sticks; 410 StickArray sticks;
411 VibrationArray vibrations;
407 MotionArray motions; 412 MotionArray motions;
408 std::vector<u32> supported_npad_id_types{}; 413 std::vector<u32> supported_npad_id_types{};
409 NpadHoldType hold_type{NpadHoldType::Vertical}; 414 NpadHoldType hold_type{NpadHoldType::Vertical};
410 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 415 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
411 // Each controller should have their own styleset changed event 416 // Each controller should have their own styleset changed event
412 std::array<Kernel::EventPair, 10> styleset_changed_events; 417 std::array<Kernel::EventPair, 10> styleset_changed_events;
413 std::array<std::array<VibrationValue, 3>, 10> latest_vibration_values; 418 std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
414 std::array<ControllerHolder, 10> connected_controllers{}; 419 std::array<ControllerHolder, 10> connected_controllers{};
415 std::array<bool, 10> unintended_home_button_input_protection{}; 420 std::array<bool, 10> unintended_home_button_input_protection{};
416 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 421 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) {
998 break; 998 break;
999 case Controller_NPad::DeviceIndex::None: 999 case Controller_NPad::DeviceIndex::None:
1000 default: 1000 default:
1001 UNREACHABLE_MSG("DeviceIndex should never be None!");
1001 vibration_device_info.position = VibrationDevicePosition::None; 1002 vibration_device_info.position = VibrationDevicePosition::None;
1002 break; 1003 break;
1003 } 1004 }
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() {
230 vibration_changed = false; 230 vibration_changed = false;
231} 231}
232 232
233bool Adapter::RumblePlay(std::size_t port, f32 amplitude) { 233bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
234 amplitude = std::clamp(amplitude, 0.0f, 1.0f); 234 pads[port].rumble_amplitude = amplitude;
235 const auto raw_amp = static_cast<u8>(amplitude * 0x8);
236 pads[port].rumble_amplitude = raw_amp;
237 235
238 return rumble_enabled; 236 return rumble_enabled;
239} 237}
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:
77 Adapter(); 77 Adapter();
78 ~Adapter(); 78 ~Adapter();
79 79
80 /// Request a vibration for a controlelr 80 /// Request a vibration for a controller
81 bool RumblePlay(std::size_t port, f32 amplitude); 81 bool RumblePlay(std::size_t port, u8 amplitude);
82 82
83 /// Used for polling 83 /// Used for polling
84 void BeginConfiguration(); 84 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 {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter) 18 explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
@@ -27,18 +27,10 @@ public:
27 return false; 27 return false;
28 } 28 }
29 29
30 bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
31 const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
32 const auto new_amp =
33 static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
34
35 return gcadapter->RumblePlay(port, new_amp);
36 }
37
38private: 30private:
39 const u32 port; 31 const u32 port;
40 const s32 button; 32 const s32 button;
41 GCAdapter::Adapter* gcadapter; 33 const GCAdapter::Adapter* gcadapter;
42}; 34};
43 35
44class GCAxisButton final : public Input::ButtonDevice { 36class GCAxisButton final : public Input::ButtonDevice {
@@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
299 return params; 291 return params;
300} 292}
301 293
294class GCVibration final : public Input::VibrationDevice {
295public:
296 explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
297 : port(port_), gcadapter(adapter) {}
298
299 u8 GetStatus() const override {
300 return gcadapter->RumblePlay(port, 0);
301 }
302
303 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
304 const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
305 const auto processed_amplitude = static_cast<u8>(
306 pow(mean_amplitude, 0.5f) * (3.0f - 2.0f * pow(mean_amplitude, 0.15f)) * 0x8);
307
308 return gcadapter->RumblePlay(port, processed_amplitude);
309 }
310
311private:
312 const u32 port;
313 GCAdapter::Adapter* gcadapter;
314};
315
316/// An vibration device factory that creates vibration devices from GC Adapter
317GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
318 : adapter(std::move(adapter_)) {}
319
320/**
321 * Creates a vibration device from a joystick
322 * @param params contains parameters for creating the device:
323 * - "port": the nth gcpad on the adapter
324 */
325std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
326 const Common::ParamPackage& params) {
327 const auto port = static_cast<u32>(params.Get("port", 0));
328
329 return std::make_unique<GCVibration>(port, adapter.get());
330}
331
302} // namespace InputCommon 332} // 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:
64 bool polling = false; 64 bool polling = false;
65}; 65};
66 66
67/// A vibration device factory creates vibration devices from GC Adapter
68class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
69public:
70 explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
71
72 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
73
74private:
75 std::shared_ptr<GCAdapter::Adapter> adapter;
76};
77
67} // namespace InputCommon 78} // 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 {
28 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); 28 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
29 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); 29 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
30 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); 30 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
31 gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
32 Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
31 33
32 keyboard = std::make_shared<Keyboard>(); 34 keyboard = std::make_shared<Keyboard>();
33 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 35 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@@ -64,9 +66,11 @@ struct InputSubsystem::Impl {
64#endif 66#endif
65 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 67 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
66 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 68 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
69 Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
67 70
68 gcbuttons.reset(); 71 gcbuttons.reset();
69 gcanalog.reset(); 72 gcanalog.reset();
73 gcvibration.reset();
70 74
71 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 75 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
72 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); 76 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
@@ -142,6 +146,7 @@ struct InputSubsystem::Impl {
142#endif 146#endif
143 std::shared_ptr<GCButtonFactory> gcbuttons; 147 std::shared_ptr<GCButtonFactory> gcbuttons;
144 std::shared_ptr<GCAnalogFactory> gcanalog; 148 std::shared_ptr<GCAnalogFactory> gcanalog;
149 std::shared_ptr<GCVibrationFactory> gcvibration;
145 std::shared_ptr<UDPMotionFactory> udpmotion; 150 std::shared_ptr<UDPMotionFactory> udpmotion;
146 std::shared_ptr<UDPTouchFactory> udptouch; 151 std::shared_ptr<UDPTouchFactory> udptouch;
147 std::shared_ptr<CemuhookUDP::Client> udp; 152 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:
85 using std::chrono::milliseconds; 85 using std::chrono::milliseconds;
86 using std::chrono::steady_clock; 86 using std::chrono::steady_clock;
87 87
88 // Prevent vibrations less than 10ms apart from each other. 88 // Block non-zero vibrations less than 10ms apart from each other.
89 if (duration_cast<milliseconds>(steady_clock::now() - last_vibration) < milliseconds(10)) { 89 if ((amp_low != 0 || amp_high != 0) &&
90 duration_cast<milliseconds>(steady_clock::now() - last_vibration) < milliseconds(10)) {
90 return false; 91 return false;
91 }; 92 }
92 93
93 last_vibration = steady_clock::now(); 94 last_vibration = steady_clock::now();
94 95
95 if (sdl_controller != nullptr) { 96 if (sdl_controller) {
96 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; 97 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
97 } else if (sdl_joystick != nullptr) { 98 } else if (sdl_joystick) {
98 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; 99 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
99 } 100 }
100 101
@@ -321,14 +322,6 @@ public:
321 return joystick->GetButton(button); 322 return joystick->GetButton(button);
322 } 323 }
323 324
324 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
325 const u16 processed_amp_low =
326 static_cast<u16>(pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)) * 0xFFFF);
327 const u16 processed_amp_high =
328 static_cast<u16>(pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)) * 0xFFFF);
329 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
330 }
331
332private: 325private:
333 std::shared_ptr<SDLJoystick> joystick; 326 std::shared_ptr<SDLJoystick> joystick;
334 int button; 327 int button;
@@ -412,6 +405,32 @@ private:
412 const float range; 405 const float range;
413}; 406};
414 407
408class SDLVibration final : public Input::VibrationDevice {
409public:
410 explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
411 : joystick(std::move(joystick_)) {}
412
413 u8 GetStatus() const override {
414 joystick->RumblePlay(1, 1);
415 return joystick->RumblePlay(0, 0);
416 }
417
418 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
419 const auto process_amplitude = [](f32 amplitude) {
420 return static_cast<u16>(std::pow(amplitude, 0.5f) *
421 (3.0f - 2.0f * std::pow(amplitude, 0.15f)) * 0xFFFF);
422 };
423
424 const auto processed_amp_low = process_amplitude(amp_low);
425 const auto processed_amp_high = process_amplitude(amp_high);
426
427 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
428 }
429
430private:
431 std::shared_ptr<SDLJoystick> joystick;
432};
433
415class SDLDirectionMotion final : public Input::MotionDevice { 434class SDLDirectionMotion final : public Input::MotionDevice {
416public: 435public:
417 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) 436 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@@ -554,7 +573,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
554public: 573public:
555 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} 574 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
556 /** 575 /**
557 * Creates analog device from joystick axes 576 * Creates an analog device from joystick axes
558 * @param params contains parameters for creating the device: 577 * @param params contains parameters for creating the device:
559 * - "guid": the guid of the joystick to bind 578 * - "guid": the guid of the joystick to bind
560 * - "port": the nth joystick of the same type 579 * - "port": the nth joystick of the same type
@@ -580,6 +599,26 @@ private:
580 SDLState& state; 599 SDLState& state;
581}; 600};
582 601
602/// An vibration device factory that creates vibration devices from SDL joystick
603class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
604public:
605 explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
606 /**
607 * Creates a vibration device from a joystick
608 * @param params contains parameters for creating the device:
609 * - "guid": the guid of the joystick to bind
610 * - "port": the nth joystick of the same type
611 */
612 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
613 const std::string guid = params.Get("guid", "0");
614 const int port = params.Get("port", 0);
615 return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
616 }
617
618private:
619 SDLState& state;
620};
621
583/// A motion device factory that creates motion devices from SDL joystick 622/// A motion device factory that creates motion devices from SDL joystick
584class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> { 623class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
585public: 624public:
@@ -646,11 +685,13 @@ private:
646 685
647SDLState::SDLState() { 686SDLState::SDLState() {
648 using namespace Input; 687 using namespace Input;
649 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
650 button_factory = std::make_shared<SDLButtonFactory>(*this); 688 button_factory = std::make_shared<SDLButtonFactory>(*this);
689 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
690 vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
651 motion_factory = std::make_shared<SDLMotionFactory>(*this); 691 motion_factory = std::make_shared<SDLMotionFactory>(*this);
652 RegisterFactory<AnalogDevice>("sdl", analog_factory);
653 RegisterFactory<ButtonDevice>("sdl", button_factory); 692 RegisterFactory<ButtonDevice>("sdl", button_factory);
693 RegisterFactory<AnalogDevice>("sdl", analog_factory);
694 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
654 RegisterFactory<MotionDevice>("sdl", motion_factory); 695 RegisterFactory<MotionDevice>("sdl", motion_factory);
655 696
656 // If the frontend is going to manage the event loop, then we don't start one here 697 // If the frontend is going to manage the event loop, then we don't start one here
@@ -687,6 +728,7 @@ SDLState::~SDLState() {
687 using namespace Input; 728 using namespace Input;
688 UnregisterFactory<ButtonDevice>("sdl"); 729 UnregisterFactory<ButtonDevice>("sdl");
689 UnregisterFactory<AnalogDevice>("sdl"); 730 UnregisterFactory<AnalogDevice>("sdl");
731 UnregisterFactory<VibrationDevice>("sdl");
690 UnregisterFactory<MotionDevice>("sdl"); 732 UnregisterFactory<MotionDevice>("sdl");
691 733
692 CloseJoysticks(); 734 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 {
22class SDLAnalogFactory; 22class SDLAnalogFactory;
23class SDLButtonFactory; 23class SDLButtonFactory;
24class SDLMotionFactory; 24class SDLMotionFactory;
25class SDLVibrationFactory;
25class SDLJoystick; 26class SDLJoystick;
26 27
27class SDLState : public State { 28class SDLState : public State {
@@ -72,6 +73,7 @@ private:
72 73
73 std::shared_ptr<SDLButtonFactory> button_factory; 74 std::shared_ptr<SDLButtonFactory> button_factory;
74 std::shared_ptr<SDLAnalogFactory> analog_factory; 75 std::shared_ptr<SDLAnalogFactory> analog_factory;
76 std::shared_ptr<SDLVibrationFactory> vibration_factory;
75 std::shared_ptr<SDLMotionFactory> motion_factory; 77 std::shared_ptr<SDLMotionFactory> motion_factory;
76 78
77 bool start_thread = false; 79 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 = {{
14}}; 14}};
15} 15}
16 16
17namespace NativeMotion {
18const std::array<const char*, NumMotions> mapping = {{
19 "motionleft",
20 "motionright",
21}};
22}
23
24namespace NativeAnalog { 17namespace NativeAnalog {
25const std::array<const char*, NumAnalogs> mapping = {{ 18const std::array<const char*, NumAnalogs> mapping = {{
26 "lstick", 19 "lstick",
@@ -28,6 +21,20 @@ const std::array<const char*, NumAnalogs> mapping = {{
28}}; 21}};
29} 22}
30 23
24namespace NativeVibration {
25const std::array<const char*, NumVibrations> mapping = {{
26 "left_vibration_device",
27 "right_vibration_device",
28}};
29}
30
31namespace NativeMotion {
32const std::array<const char*, NumMotions> mapping = {{
33 "motionleft",
34 "motionright",
35}};
36}
37
31namespace NativeMouseButton { 38namespace NativeMouseButton {
32const std::array<const char*, NumMouseButtons> mapping = {{ 39const std::array<const char*, NumMouseButtons> mapping = {{
33 "left", 40 "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;
66extern const std::array<const char*, NumAnalogs> mapping; 66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog 67} // namespace NativeAnalog
68 68
69namespace NativeVibration {
70enum Values : int {
71 LeftVibrationDevice,
72 RightVibrationDevice,
73
74 NumVibrations,
75};
76
77constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice;
78constexpr int VIBRATION_HID_END = NumVibrations;
79constexpr int NUM_VIBRATIONS_HID = NumVibrations;
80
81extern const std::array<const char*, NumVibrations> mapping;
82}; // namespace NativeVibration
83
69namespace NativeMotion { 84namespace NativeMotion {
70enum Values : int { 85enum Values : int {
71 MOTIONLEFT, 86 MotionLeft,
72 MOTIONRIGHT, 87 MotionRight,
73 88
74 NumMotions, 89 NumMotions,
75}; 90};
76 91
77constexpr int MOTION_HID_BEGIN = MOTIONLEFT; 92constexpr int MOTION_HID_BEGIN = MotionLeft;
78constexpr int MOTION_HID_END = NumMotions; 93constexpr int MOTION_HID_END = NumMotions;
79constexpr int NUM_MOTION_HID = NumMotions; 94constexpr int NUM_MOTIONS_HID = NumMotions;
80 95
81extern const std::array<const char*, NumMotions> mapping; 96extern const std::array<const char*, NumMotions> mapping;
82} // namespace NativeMotion 97} // namespace NativeMotion
@@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
305 320
306} // namespace NativeKeyboard 321} // namespace NativeKeyboard
307 322
308using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
309using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 323using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
310using MotionRaw = std::array<std::string, NativeMotion::NumMotions>; 324using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
325using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
326using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
327
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; 328using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; 329using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; 330using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -330,7 +347,8 @@ struct PlayerInput {
330 ControllerType controller_type; 347 ControllerType controller_type;
331 ButtonsRaw buttons; 348 ButtonsRaw buttons;
332 AnalogsRaw analogs; 349 AnalogsRaw analogs;
333 MotionRaw motions; 350 VibrationsRaw vibrations;
351 MotionsRaw motions;
334 352
335 bool vibration_enabled; 353 bool vibration_enabled;
336 int vibration_strength; 354 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)
478 return; 478 return;
479 } 479 }
480 480
481 ConfigureVibration::SetVibrationDevices(player_index);
482
481 // Player 1 and Handheld 483 // Player 1 and Handheld
482 auto& handheld = Settings::values.players.GetValue()[8]; 484 auto& handheld = Settings::values.players.GetValue()[8];
483 // If Handheld is selected, copy all the settings from Player 1 to Handheld. 485 // 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) {
344 } 344 }
345 } 345 }
346 346
347 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
348 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
349 auto& player_motions = player.motions[i];
350
351 player_motions = qt_config
352 ->value(QStringLiteral("%1").arg(player_prefix) +
353 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
354 QString::fromStdString(default_param))
355 .toString()
356 .toStdString();
357 if (player_motions.empty()) {
358 player_motions = default_param;
359 }
360 }
361
362 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 347 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
363 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 348 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
364 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 349 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -375,6 +360,33 @@ void Config::ReadPlayerValue(std::size_t player_index) {
375 player_analogs = default_param; 360 player_analogs = default_param;
376 } 361 }
377 } 362 }
363
364 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
365 auto& player_vibrations = player.vibrations[i];
366
367 player_vibrations =
368 qt_config
369 ->value(QStringLiteral("%1").arg(player_prefix) +
370 QString::fromUtf8(Settings::NativeVibration::mapping[i]),
371 QString{})
372 .toString()
373 .toStdString();
374 }
375
376 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
377 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
378 auto& player_motions = player.motions[i];
379
380 player_motions = qt_config
381 ->value(QStringLiteral("%1").arg(player_prefix) +
382 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
383 QString::fromStdString(default_param))
384 .toString()
385 .toStdString();
386 if (player_motions.empty()) {
387 player_motions = default_param;
388 }
389 }
378} 390}
379 391
380void Config::ReadDebugValues() { 392void Config::ReadDebugValues() {
@@ -1014,13 +1026,6 @@ void Config::SavePlayerValue(std::size_t player_index) {
1014 QString::fromStdString(player.buttons[i]), 1026 QString::fromStdString(player.buttons[i]),
1015 QString::fromStdString(default_param)); 1027 QString::fromStdString(default_param));
1016 } 1028 }
1017 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
1018 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
1019 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
1020 QString::fromStdString(Settings::NativeMotion::mapping[i]),
1021 QString::fromStdString(player.motions[i]),
1022 QString::fromStdString(default_param));
1023 }
1024 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 1029 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
1025 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 1030 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
1026 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 1031 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -1030,6 +1035,18 @@ void Config::SavePlayerValue(std::size_t player_index) {
1030 QString::fromStdString(player.analogs[i]), 1035 QString::fromStdString(player.analogs[i]),
1031 QString::fromStdString(default_param)); 1036 QString::fromStdString(default_param));
1032 } 1037 }
1038 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
1039 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
1040 QString::fromStdString(Settings::NativeVibration::mapping[i]),
1041 QString::fromStdString(player.vibrations[i]), QString{});
1042 }
1043 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
1044 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
1045 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
1046 QString::fromStdString(Settings::NativeMotion::mapping[i]),
1047 QString::fromStdString(player.motions[i]),
1048 QString::fromStdString(default_param));
1049 }
1033} 1050}
1034 1051
1035void Config::SaveDebugValues() { 1052void 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 @@
22#include "ui_configure_input_player.h" 22#include "ui_configure_input_player.h"
23#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input_player.h" 24#include "yuzu/configuration/configure_input_player.h"
25#include "yuzu/configuration/configure_vibration.h"
25#include "yuzu/configuration/input_profiles.h" 26#include "yuzu/configuration/input_profiles.h"
26#include "yuzu/util/limitable_input_dialog.h" 27#include "yuzu/util/limitable_input_dialog.h"
27 28
@@ -39,6 +40,10 @@ namespace {
39 40
40void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, 41void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
41 bool connected) { 42 bool connected) {
43 auto& player = Settings::values.players.GetValue()[npad_index];
44 player.controller_type = controller_type;
45 player.connected = connected;
46
42 Core::System& system{Core::System::GetInstance()}; 47 Core::System& system{Core::System::GetInstance()};
43 if (!system.IsPoweredOn()) { 48 if (!system.IsPoweredOn()) {
44 return; 49 return;
@@ -565,6 +570,8 @@ void ConfigureInputPlayer::ApplyConfiguration() {
565 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); 570 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
566 player.connected = ui->groupConnectedController->isChecked(); 571 player.connected = ui->groupConnectedController->isChecked();
567 572
573 ConfigureVibration::SetVibrationDevices(player_index);
574
568 // Player 2-8 575 // Player 2-8
569 if (player_index != 0) { 576 if (player_index != 0) {
570 UpdateController(player.controller_type, player_index, player.connected); 577 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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <unordered_map>
7
8#include <fmt/format.h>
9
10#include "common/param_package.h"
5#include "core/settings.h" 11#include "core/settings.h"
6#include "ui_configure_vibration.h" 12#include "ui_configure_vibration.h"
7#include "yuzu/configuration/configure_vibration.h" 13#include "yuzu/configuration/configure_vibration.h"
@@ -53,6 +59,80 @@ void ConfigureVibration::ApplyConfiguration() {
53 ui->checkBoxAccurateVibration->isChecked()); 59 ui->checkBoxAccurateVibration->isChecked());
54} 60}
55 61
62void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
63 using namespace Settings::NativeButton;
64 static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
65 {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
66 {A, B, X, Y, R, ZR}, // Right Buttons
67 }};
68
69 auto& player = Settings::values.players.GetValue()[player_index];
70
71 for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
72 std::unordered_map<std::string, int> params_count;
73
74 for (const auto button_index : buttons[device_idx]) {
75 const auto& player_button = player.buttons[button_index];
76
77 if (params_count.find(player_button) != params_count.end()) {
78 ++params_count[player_button];
79 continue;
80 }
81
82 params_count.insert_or_assign(player_button, 1);
83 }
84
85 const auto it = std::max_element(
86 params_count.begin(), params_count.end(),
87 [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
88
89 auto& vibration_param_str = player.vibrations[device_idx];
90 vibration_param_str.clear();
91
92 if (it->first.empty()) {
93 continue;
94 }
95
96 const auto param = Common::ParamPackage(it->first);
97
98 const auto engine = param.Get("engine", "");
99 const auto guid = param.Get("guid", "");
100 const auto port = param.Get("port", "");
101
102 if (engine.empty() || engine == "keyboard") {
103 continue;
104 }
105
106 vibration_param_str += fmt::format("engine:{}", engine);
107
108 if (!port.empty()) {
109 vibration_param_str += fmt::format(",port:{}", port);
110 }
111 if (!guid.empty()) {
112 vibration_param_str += fmt::format(",guid:{}", guid);
113 }
114 }
115
116 if (player.vibrations[0] != player.vibrations[1]) {
117 return;
118 }
119
120 if (!player.vibrations[0].empty() &&
121 player.controller_type != Settings::ControllerType::RightJoycon) {
122 player.vibrations[1].clear();
123 } else if (!player.vibrations[1].empty() &&
124 player.controller_type == Settings::ControllerType::RightJoycon) {
125 player.vibrations[0].clear();
126 }
127}
128
129void ConfigureVibration::SetAllVibrationDevices() {
130 // Set vibration devices for all player indices including handheld
131 for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
132 SetVibrationDevices(player_idx);
133 }
134}
135
56void ConfigureVibration::changeEvent(QEvent* event) { 136void ConfigureVibration::changeEvent(QEvent* event) {
57 if (event->type() == QEvent::LanguageChange) { 137 if (event->type() == QEvent::LanguageChange) {
58 RetranslateUI(); 138 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:
24 24
25 void ApplyConfiguration(); 25 void ApplyConfiguration();
26 26
27 static void SetVibrationDevices(std::size_t player_index);
28 static void SetAllVibrationDevices();
29
27private: 30private:
28 void changeEvent(QEvent* event) override; 31 void changeEvent(QEvent* event) override;
29 void RetranslateUI(); 32 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 @@
18#include "applets/web_browser.h" 18#include "applets/web_browser.h"
19#include "configuration/configure_input.h" 19#include "configuration/configure_input.h"
20#include "configuration/configure_per_game.h" 20#include "configuration/configure_per_game.h"
21#include "configuration/configure_vibration.h"
21#include "core/file_sys/vfs.h" 22#include "core/file_sys/vfs.h"
22#include "core/file_sys/vfs_real.h" 23#include "core/file_sys/vfs_real.h"
23#include "core/frontend/applets/controller.h" 24#include "core/frontend/applets/controller.h"
@@ -1096,6 +1097,8 @@ void GMainWindow::BootGame(const QString& filename) {
1096 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); 1097 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
1097 } 1098 }
1098 1099
1100 ConfigureVibration::SetAllVibrationDevices();
1101
1099 Settings::LogSettings(); 1102 Settings::LogSettings();
1100 1103
1101 if (UISettings::values.select_user_on_boot) { 1104 if (UISettings::values.select_user_on_boot) {