yuzu-emu
/
yuzu-android
Archived
1
0
Fork 0

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.
This commit is contained in:
Morph 2020-10-20 13:55:25 -04:00
parent 38110dd485
commit e9e1876e82
19 changed files with 327 additions and 101 deletions

View File

@ -121,6 +121,13 @@ using ButtonDevice = InputDevice<bool>;
*/ */
using AnalogDevice = InputDevice<std::tuple<float, float>>; 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, * A motion status is an object that returns a tuple of accelerometer state vector,
* gyroscope state vector, rotation state vector and orientation state matrix. * gyroscope state vector, rotation state vector and orientation state matrix.

View File

@ -271,6 +271,10 @@ void Controller_NPad::OnLoadInputDevices() {
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); 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, std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
motions[i].begin(), Input::CreateDevice<Input::MotionDevice>); motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
@ -278,8 +282,10 @@ void Controller_NPad::OnLoadInputDevices() {
} }
void Controller_NPad::OnRelease() { void Controller_NPad::OnRelease() {
for (std::size_t index = 0; index < connected_controllers.size(); ++index) { for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
VibrateControllerAtIndex(index, {}); 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) { 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; return false;
} }
@ -686,10 +692,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index,
return false; return false;
} }
using namespace Settings::NativeButton; return vibrations[npad_index][device_index]->SetRumblePlay(
const auto& button_state = buttons[npad_index];
return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f),
vibration_value.freq_low, vibration_value.freq_low,
std::min(vibration_value.amp_high * player.vibration_strength / 100.0f, 1.0f), 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; 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. // Some games try to send mismatched parameters in the device handle, block these.
if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
(vibration_device_handles[i].npad_type == NpadType::JoyconRight || (vibration_device_handles[i].npad_type == NpadType::JoyconRight ||
@ -747,28 +755,8 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
continue; continue;
} }
// TODO: Vibrate left/right vibration motors independently if possible. if (VibrateControllerAtIndex(npad_index, device_index, vibration_values[i])) {
if (VibrateControllerAtIndex(npad_index, vibration_values[i])) { latest_vibration_values[npad_index][device_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;
}
} }
} }
} }

View File

@ -148,7 +148,8 @@ public:
void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); 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, void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
const std::vector<VibrationValue>& vibration_values); const std::vector<VibrationValue>& vibration_values);
@ -399,18 +400,22 @@ private:
using StickArray = std::array< using StickArray = std::array<
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
10>; 10>;
using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
Settings::NativeVibration::NUM_VIBRATIONS_HID>,
10>;
using MotionArray = std::array< 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>; 10>;
ButtonArray buttons; ButtonArray buttons;
StickArray sticks; StickArray sticks;
VibrationArray vibrations;
MotionArray motions; MotionArray motions;
std::vector<u32> supported_npad_id_types{}; std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical}; NpadHoldType hold_type{NpadHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
// Each controller should have their own styleset changed event // Each controller should have their own styleset changed event
std::array<Kernel::EventPair, 10> styleset_changed_events; 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<ControllerHolder, 10> connected_controllers{};
std::array<bool, 10> unintended_home_button_input_protection{}; std::array<bool, 10> unintended_home_button_input_protection{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};

View File

@ -998,6 +998,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
break; break;
case Controller_NPad::DeviceIndex::None: case Controller_NPad::DeviceIndex::None:
default: default:
UNREACHABLE_MSG("DeviceIndex should never be None!");
vibration_device_info.position = VibrationDevicePosition::None; vibration_device_info.position = VibrationDevicePosition::None;
break; break;
} }

View File

@ -230,10 +230,8 @@ void Adapter::SendVibrations() {
vibration_changed = false; vibration_changed = false;
} }
bool Adapter::RumblePlay(std::size_t port, f32 amplitude) { bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
amplitude = std::clamp(amplitude, 0.0f, 1.0f); pads[port].rumble_amplitude = amplitude;
const auto raw_amp = static_cast<u8>(amplitude * 0x8);
pads[port].rumble_amplitude = raw_amp;
return rumble_enabled; return rumble_enabled;
} }

View File

@ -77,8 +77,8 @@ public:
Adapter(); Adapter();
~Adapter(); ~Adapter();
/// Request a vibration for a controlelr /// Request a vibration for a controller
bool RumblePlay(std::size_t port, f32 amplitude); bool RumblePlay(std::size_t port, u8 amplitude);
/// Used for polling /// Used for polling
void BeginConfiguration(); void BeginConfiguration();

View File

@ -15,7 +15,7 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice { class GCButton final : public Input::ButtonDevice {
public: 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) {} : port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override; ~GCButton() override;
@ -27,18 +27,10 @@ public:
return false; 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: private:
const u32 port; const u32 port;
const s32 button; const s32 button;
GCAdapter::Adapter* gcadapter; const GCAdapter::Adapter* gcadapter;
}; };
class GCAxisButton final : public Input::ButtonDevice { class GCAxisButton final : public Input::ButtonDevice {
@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
return params; 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 } // namespace InputCommon

View File

@ -64,4 +64,15 @@ private:
bool polling = false; 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 } // namespace InputCommon

View File

@ -28,6 +28,8 @@ struct InputSubsystem::Impl {
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
keyboard = std::make_shared<Keyboard>(); keyboard = std::make_shared<Keyboard>();
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@ -64,9 +66,11 @@ struct InputSubsystem::Impl {
#endif #endif
Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
gcbuttons.reset(); gcbuttons.reset();
gcanalog.reset(); gcanalog.reset();
gcvibration.reset();
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
@ -142,6 +146,7 @@ struct InputSubsystem::Impl {
#endif #endif
std::shared_ptr<GCButtonFactory> gcbuttons; std::shared_ptr<GCButtonFactory> gcbuttons;
std::shared_ptr<GCAnalogFactory> gcanalog; std::shared_ptr<GCAnalogFactory> gcanalog;
std::shared_ptr<GCVibrationFactory> gcvibration;
std::shared_ptr<UDPMotionFactory> udpmotion; std::shared_ptr<UDPMotionFactory> udpmotion;
std::shared_ptr<UDPTouchFactory> udptouch; std::shared_ptr<UDPTouchFactory> udptouch;
std::shared_ptr<CemuhookUDP::Client> udp; std::shared_ptr<CemuhookUDP::Client> udp;

View File

@ -85,16 +85,17 @@ public:
using std::chrono::milliseconds; using std::chrono::milliseconds;
using std::chrono::steady_clock; using std::chrono::steady_clock;
// Prevent vibrations less than 10ms apart from each other. // Block non-zero vibrations less than 10ms apart from each other.
if (duration_cast<milliseconds>(steady_clock::now() - last_vibration) < milliseconds(10)) { if ((amp_low != 0 || amp_high != 0) &&
duration_cast<milliseconds>(steady_clock::now() - last_vibration) < milliseconds(10)) {
return false; return false;
}; }
last_vibration = steady_clock::now(); last_vibration = steady_clock::now();
if (sdl_controller != nullptr) { if (sdl_controller) {
return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; 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; return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
} }
@ -321,14 +322,6 @@ public:
return joystick->GetButton(button); 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: private:
std::shared_ptr<SDLJoystick> joystick; std::shared_ptr<SDLJoystick> joystick;
int button; int button;
@ -412,6 +405,32 @@ private:
const float range; 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 { class SDLDirectionMotion final : public Input::MotionDevice {
public: public:
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@ -554,7 +573,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public: public:
explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} 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: * @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind * - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type * - "port": the nth joystick of the same type
@ -580,6 +599,26 @@ private:
SDLState& state; 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 /// A motion device factory that creates motion devices from SDL joystick
class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> { class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
public: public:
@ -646,11 +685,13 @@ private:
SDLState::SDLState() { SDLState::SDLState() {
using namespace Input; using namespace Input;
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
button_factory = std::make_shared<SDLButtonFactory>(*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); motion_factory = std::make_shared<SDLMotionFactory>(*this);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<ButtonDevice>("sdl", button_factory); RegisterFactory<ButtonDevice>("sdl", button_factory);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory); RegisterFactory<MotionDevice>("sdl", motion_factory);
// If the frontend is going to manage the event loop, then we don't start one here // 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; using namespace Input;
UnregisterFactory<ButtonDevice>("sdl"); UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl"); UnregisterFactory<AnalogDevice>("sdl");
UnregisterFactory<VibrationDevice>("sdl");
UnregisterFactory<MotionDevice>("sdl"); UnregisterFactory<MotionDevice>("sdl");
CloseJoysticks(); CloseJoysticks();

View File

@ -22,6 +22,7 @@ namespace InputCommon::SDL {
class SDLAnalogFactory; class SDLAnalogFactory;
class SDLButtonFactory; class SDLButtonFactory;
class SDLMotionFactory; class SDLMotionFactory;
class SDLVibrationFactory;
class SDLJoystick; class SDLJoystick;
class SDLState : public State { class SDLState : public State {
@ -72,6 +73,7 @@ private:
std::shared_ptr<SDLButtonFactory> button_factory; std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory; std::shared_ptr<SDLAnalogFactory> analog_factory;
std::shared_ptr<SDLVibrationFactory> vibration_factory;
std::shared_ptr<SDLMotionFactory> motion_factory; std::shared_ptr<SDLMotionFactory> motion_factory;
bool start_thread = false; bool start_thread = false;

View File

@ -14,13 +14,6 @@ const std::array<const char*, NumButtons> mapping = {{
}}; }};
} }
namespace NativeMotion {
const std::array<const char*, NumMotions> mapping = {{
"motionleft",
"motionright",
}};
}
namespace NativeAnalog { namespace NativeAnalog {
const std::array<const char*, NumAnalogs> mapping = {{ const std::array<const char*, NumAnalogs> mapping = {{
"lstick", "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 { namespace NativeMouseButton {
const std::array<const char*, NumMouseButtons> mapping = {{ const std::array<const char*, NumMouseButtons> mapping = {{
"left", "left",

View File

@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
extern const std::array<const char*, NumAnalogs> mapping; extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog } // 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 { namespace NativeMotion {
enum Values : int { enum Values : int {
MOTIONLEFT, MotionLeft,
MOTIONRIGHT, MotionRight,
NumMotions, NumMotions,
}; };
constexpr int MOTION_HID_BEGIN = MOTIONLEFT; constexpr int MOTION_HID_BEGIN = MotionLeft;
constexpr int MOTION_HID_END = NumMotions; 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; extern const std::array<const char*, NumMotions> mapping;
} // namespace NativeMotion } // namespace NativeMotion
@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
} // namespace NativeKeyboard } // namespace NativeKeyboard
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 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 MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@ -330,7 +347,8 @@ struct PlayerInput {
ControllerType controller_type; ControllerType controller_type;
ButtonsRaw buttons; ButtonsRaw buttons;
AnalogsRaw analogs; AnalogsRaw analogs;
MotionRaw motions; VibrationsRaw vibrations;
MotionsRaw motions;
bool vibration_enabled; bool vibration_enabled;
int vibration_strength; int vibration_strength;

View File

@ -478,6 +478,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
return; return;
} }
ConfigureVibration::SetVibrationDevices(player_index);
// Player 1 and Handheld // Player 1 and Handheld
auto& handheld = Settings::values.players.GetValue()[8]; auto& handheld = Settings::values.players.GetValue()[8];
// If Handheld is selected, copy all the settings from Player 1 to Handheld. // If Handheld is selected, copy all the settings from Player 1 to Handheld.

View File

@ -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) { for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 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; 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() { void Config::ReadDebugValues() {
@ -1014,13 +1026,6 @@ void Config::SavePlayerValue(std::size_t player_index) {
QString::fromStdString(player.buttons[i]), QString::fromStdString(player.buttons[i]),
QString::fromStdString(default_param)); 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) { for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 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(player.analogs[i]),
QString::fromStdString(default_param)); 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() { void Config::SaveDebugValues() {

View File

@ -22,6 +22,7 @@
#include "ui_configure_input_player.h" #include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h" #include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h" #include "yuzu/configuration/input_profiles.h"
#include "yuzu/util/limitable_input_dialog.h" #include "yuzu/util/limitable_input_dialog.h"
@ -39,6 +40,10 @@ namespace {
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
bool connected) { bool connected) {
auto& player = Settings::values.players.GetValue()[npad_index];
player.controller_type = controller_type;
player.connected = connected;
Core::System& system{Core::System::GetInstance()}; Core::System& system{Core::System::GetInstance()};
if (!system.IsPoweredOn()) { if (!system.IsPoweredOn()) {
return; return;
@ -565,6 +570,8 @@ void ConfigureInputPlayer::ApplyConfiguration() {
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
player.connected = ui->groupConnectedController->isChecked(); player.connected = ui->groupConnectedController->isChecked();
ConfigureVibration::SetVibrationDevices(player_index);
// Player 2-8 // Player 2-8
if (player_index != 0) { if (player_index != 0) {
UpdateController(player.controller_type, player_index, player.connected); UpdateController(player.controller_type, player_index, player.connected);

View File

@ -2,6 +2,12 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // 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 "core/settings.h"
#include "ui_configure_vibration.h" #include "ui_configure_vibration.h"
#include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/configure_vibration.h"
@ -53,6 +59,80 @@ void ConfigureVibration::ApplyConfiguration() {
ui->checkBoxAccurateVibration->isChecked()); 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) { void ConfigureVibration::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) { if (event->type() == QEvent::LanguageChange) {
RetranslateUI(); RetranslateUI();

View File

@ -24,6 +24,9 @@ public:
void ApplyConfiguration(); void ApplyConfiguration();
static void SetVibrationDevices(std::size_t player_index);
static void SetAllVibrationDevices();
private: private:
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;
void RetranslateUI(); void RetranslateUI();

View File

@ -18,6 +18,7 @@
#include "applets/web_browser.h" #include "applets/web_browser.h"
#include "configuration/configure_input.h" #include "configuration/configure_input.h"
#include "configuration/configure_per_game.h" #include "configuration/configure_per_game.h"
#include "configuration/configure_vibration.h"
#include "core/file_sys/vfs.h" #include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h" #include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/controller.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); Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
} }
ConfigureVibration::SetAllVibrationDevices();
Settings::LogSettings(); Settings::LogSettings();
if (UISettings::values.select_user_on_boot) { if (UISettings::values.select_user_on_boot) {