Merge pull request #4866 from Morph1984/mjolnir-p3-prod

Project Mjölnir: Part 3 - Controller Profiles and Vibration Rework
This commit is contained in:
bunnei 2020-11-17 20:02:27 -08:00 committed by GitHub
commit abda366362
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 3401 additions and 1588 deletions

View file

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

View file

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

View file

@ -15,7 +15,7 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice {
public:
explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
: port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override;
@ -27,18 +27,10 @@ public:
return false;
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
const auto new_amp =
static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
return gcadapter->RumblePlay(port, new_amp);
}
private:
const u32 port;
const s32 button;
GCAdapter::Adapter* gcadapter;
const GCAdapter::Adapter* gcadapter;
};
class GCAxisButton final : public Input::ButtonDevice {
@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
return params;
}
class GCVibration final : public Input::VibrationDevice {
public:
explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
: port(port_), gcadapter(adapter) {}
u8 GetStatus() const override {
return gcadapter->RumblePlay(port, 0);
}
bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
const auto processed_amplitude = static_cast<u8>(
pow(mean_amplitude, 0.5f) * (3.0f - 2.0f * pow(mean_amplitude, 0.15f)) * 0x8);
return gcadapter->RumblePlay(port, processed_amplitude);
}
private:
const u32 port;
GCAdapter::Adapter* gcadapter;
};
/// An vibration device factory that creates vibration devices from GC Adapter
GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
: adapter(std::move(adapter_)) {}
/**
* Creates a vibration device from a joystick
* @param params contains parameters for creating the device:
* - "port": the nth gcpad on the adapter
*/
std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
const Common::ParamPackage& params) {
const auto port = static_cast<u32>(params.Get("port", 0));
return std::make_unique<GCVibration>(port, adapter.get());
}
} // namespace InputCommon

View file

@ -64,4 +64,15 @@ private:
bool polling = false;
};
/// A vibration device factory creates vibration devices from GC Adapter
class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
public:
explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
private:
std::shared_ptr<GCAdapter::Adapter> adapter;
};
} // namespace InputCommon

View file

@ -28,6 +28,8 @@ struct InputSubsystem::Impl {
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
keyboard = std::make_shared<Keyboard>();
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@ -64,9 +66,11 @@ struct InputSubsystem::Impl {
#endif
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
gcbuttons.reset();
gcanalog.reset();
gcvibration.reset();
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
@ -78,7 +82,7 @@ struct InputSubsystem::Impl {
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
std::vector<Common::ParamPackage> devices = {
Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
};
#ifdef HAVE_SDL2
auto sdl_devices = sdl->GetInputDevices();
@ -96,10 +100,6 @@ struct InputSubsystem::Impl {
if (!params.Has("class") || params.Get("class", "") == "any") {
return {};
}
if (params.Get("class", "") == "key") {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetAnalogMappingForDevice(params);
}
@ -116,10 +116,6 @@ struct InputSubsystem::Impl {
if (!params.Has("class") || params.Get("class", "") == "any") {
return {};
}
if (params.Get("class", "") == "key") {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetButtonMappingForDevice(params);
}
@ -150,6 +146,7 @@ struct InputSubsystem::Impl {
#endif
std::shared_ptr<GCButtonFactory> gcbuttons;
std::shared_ptr<GCAnalogFactory> gcanalog;
std::shared_ptr<GCVibrationFactory> gcvibration;
std::shared_ptr<UDPMotionFactory> udpmotion;
std::shared_ptr<UDPTouchFactory> udptouch;
std::shared_ptr<CemuhookUDP::Client> udp;

View file

@ -80,30 +80,13 @@ public:
return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
}
bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) {
const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF);
const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF);
// Lower drastically the number of state changes
if (raw_amp_low >> 11 == last_state_rumble_low >> 11 &&
raw_amp_high >> 11 == last_state_rumble_high >> 11) {
if (raw_amp_low + raw_amp_high != 0 ||
last_state_rumble_low + last_state_rumble_high == 0) {
return false;
}
}
// Don't change state if last vibration was < 20ms
const auto now = std::chrono::system_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) <
std::chrono::milliseconds(20)) {
return raw_amp_low + raw_amp_high == 0;
bool RumblePlay(u16 amp_low, u16 amp_high) {
if (sdl_controller) {
return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
} else if (sdl_joystick) {
return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
}
last_vibration = now;
last_state_rumble_low = raw_amp_low;
last_state_rumble_high = raw_amp_high;
if (sdl_joystick) {
SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time);
}
return false;
}
@ -172,9 +155,6 @@ private:
} state;
std::string guid;
int port;
u16 last_state_rumble_high = 0;
u16 last_state_rumble_low = 0;
std::chrono::time_point<std::chrono::system_clock> last_vibration;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
@ -327,12 +307,6 @@ public:
return joystick->GetButton(button);
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f));
const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f));
return joystick->RumblePlay(new_amp_low, new_amp_high, 250);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int button;
@ -416,6 +390,32 @@ private:
const float range;
};
class SDLVibration final : public Input::VibrationDevice {
public:
explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
: joystick(std::move(joystick_)) {}
u8 GetStatus() const override {
joystick->RumblePlay(1, 1);
return joystick->RumblePlay(0, 0);
}
bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
const auto process_amplitude = [](f32 amplitude) {
return static_cast<u16>(std::pow(amplitude, 0.5f) *
(3.0f - 2.0f * std::pow(amplitude, 0.15f)) * 0xFFFF);
};
const auto processed_amp_low = process_amplitude(amp_low);
const auto processed_amp_high = process_amplitude(amp_high);
return joystick->RumblePlay(processed_amp_low, processed_amp_high);
}
private:
std::shared_ptr<SDLJoystick> joystick;
};
class SDLDirectionMotion final : public Input::MotionDevice {
public:
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@ -558,7 +558,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
/**
* Creates analog device from joystick axes
* Creates an analog device from joystick axes
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
@ -584,6 +584,26 @@ private:
SDLState& state;
};
/// An vibration device factory that creates vibration devices from SDL joystick
class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
public:
explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
/**
* Creates a vibration device from a joystick
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
*/
std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
}
private:
SDLState& state;
};
/// A motion device factory that creates motion devices from SDL joystick
class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
public:
@ -650,11 +670,13 @@ private:
SDLState::SDLState() {
using namespace Input;
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
button_factory = std::make_shared<SDLButtonFactory>(*this);
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
motion_factory = std::make_shared<SDLMotionFactory>(*this);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<ButtonDevice>("sdl", button_factory);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
// If the frontend is going to manage the event loop, then we don't start one here
@ -676,7 +698,7 @@ SDLState::SDLState() {
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(1ms);
}
});
}
@ -691,6 +713,7 @@ SDLState::~SDLState() {
using namespace Input;
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
UnregisterFactory<VibrationDevice>("sdl");
UnregisterFactory<MotionDevice>("sdl");
CloseJoysticks();
@ -1045,7 +1068,6 @@ public:
void Start(const std::string& device_id) override {
SDLPoller::Start(device_id);
// Load the game controller
// Reset stored axes
analog_x_axis = -1;
analog_y_axis = -1;
@ -1058,40 +1080,21 @@ public:
if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
// Simplify controller config by testing if game controller support is enabled.
if (event.type == SDL_JOYAXISMOTION) {
const auto axis = event.jaxis.axis;
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
auto* const controller = joystick->GetSDLGameController()) {
const auto axis_left_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
.value.axis;
const auto axis_left_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
.value.axis;
const auto axis_right_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
.value.axis;
const auto axis_right_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
.value.axis;
if (axis == axis_left_x || axis == axis_left_y) {
analog_x_axis = axis_left_x;
analog_y_axis = axis_left_y;
break;
} else if (axis == axis_right_x || axis == axis_right_y) {
analog_x_axis = axis_right_x;
analog_y_axis = axis_right_y;
break;
}
// In order to return a complete analog param, we need inputs for both axes.
// First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
if (analog_x_axis == -1) {
analog_x_axis = axis;
} else if (analog_y_axis == -1 && analog_x_axis != axis) {
analog_y_axis = axis;
}
} else {
// If the press wasn't accepted as a joy axis, check for a button press
auto button_press = button_poller.FromEvent(event);
if (button_press) {
return *button_press;
}
}
// If the press wasn't accepted as a joy axis, check for a button press
auto button_press = button_poller.FromEvent(event);
if (button_press) {
return *button_press;
}
}
@ -1104,6 +1107,7 @@ public:
return params;
}
}
return {};
}

View file

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

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 {
const std::array<const char*, NumAnalogs> mapping = {{
"lstick",
@ -28,6 +21,20 @@ const std::array<const char*, NumAnalogs> mapping = {{
}};
}
namespace NativeVibration {
const std::array<const char*, NumVibrations> mapping = {{
"left_vibration_device",
"right_vibration_device",
}};
}
namespace NativeMotion {
const std::array<const char*, NumMotions> mapping = {{
"motionleft",
"motionright",
}};
}
namespace NativeMouseButton {
const std::array<const char*, NumMouseButtons> mapping = {{
"left",

View file

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

View file

@ -344,7 +344,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
};
Socket socket{host, port, pad_index, client_id, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket};
const bool result = success_event.WaitFor(std::chrono::seconds(8));
const bool result = success_event.WaitFor(std::chrono::seconds(5));
socket.Stop();
worker_thread.join();
if (result) {