summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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) {