evdev: try to fix first call behaviour

We don't have all the events in the first call to get_next_button_press.
So we have to set all the buttons to max on the first call.
We also have to make sure that we don't manipulate the values if we just
want to know the connection status.
This commit is contained in:
Megamouse 2024-08-09 20:39:56 +02:00
parent 0dfc622b58
commit cb122819ae
10 changed files with 95 additions and 57 deletions

View file

@ -220,12 +220,12 @@ cfg_pad* PadHandlerBase::get_config(const std::string& pad_id)
return nullptr;
}
PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::string& pad_id, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& /*buttons*/)
PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::string& pad_id, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& /*buttons*/)
{
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
blacklist.clear();
if (first_call || get_blacklist)
if (call_type == gui_call_type::reset_input || call_type == gui_call_type::blacklist)
min_button_values.clear();
auto device = get_device(pad_id);
@ -238,7 +238,7 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri
return status;
}
if (status == connection::no_data)
if (status == connection::no_data || call_type == gui_call_type::get_connection)
{
return status;
}
@ -257,13 +257,13 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri
for (const auto& [keycode, name] : button_list)
{
if (!get_blacklist && blacklist.contains(keycode))
if (call_type != gui_call_type::blacklist && blacklist.contains(keycode))
continue;
const u16 value = data[keycode];
u16& min_value = min_button_values[keycode];
if (first_call || value < min_value)
if (call_type == gui_call_type::reset_input || value < min_value)
{
min_value = value;
continue;
@ -279,14 +279,14 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri
(is_button && (value > button_press_threshold)) ||
(is_touch_motion && (value > touch_threshold)))
{
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
{
blacklist.insert(keycode);
input_log.error("%s Calibration: Added key [ %d = %s ] to blacklist. Value = %d", m_type, keycode, name, value);
continue;
}
const u16 diff = std::abs(min_value - value);
const u16 diff = value > min_value ? value - min_value : 0;
if (diff > button_press_threshold && value > pressed_button.value)
{
@ -295,12 +295,12 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri
}
}
if (first_call)
if (call_type == gui_call_type::reset_input)
{
return connection::no_data;
}
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
{
if (blacklist.empty())
input_log.success("%s Calibration: Blacklist is clear. No input spam detected", m_type);

View file

@ -256,6 +256,14 @@ public:
pad_handler m_type;
bool m_is_init = false;
enum class gui_call_type
{
normal,
get_connection,
reset_input,
blacklist
};
std::vector<pad_ensemble>& bindings() { return m_bindings; }
std::string name_string() const { return m_name_string; }
usz max_devices() const { return m_max_devices; }
@ -287,7 +295,7 @@ public:
// Binds a Pad to a device
virtual bool bindPadToDevice(std::shared_ptr<Pad> pad);
virtual void init_config(cfg_pad* cfg) = 0;
virtual connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& buttons);
virtual connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& buttons);
virtual void get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array<AnalogSensor, 4>& sensors);
virtual std::unordered_map<u32, std::string> get_motion_axis_list() const { return {}; }

View file

@ -239,10 +239,8 @@ std::unordered_map<u64, std::pair<u16, bool>> evdev_joystick_handler::GetButtonV
if (!Init())
return button_values;
for (const auto& entry : button_list)
for (const auto& [code, name] : button_list)
{
const u32 code = entry.first;
if (code == NO_BUTTON)
continue;
@ -254,9 +252,8 @@ std::unordered_map<u64, std::pair<u16, bool>> evdev_joystick_handler::GetButtonV
button_values.emplace(code, std::make_pair<u16, bool>(static_cast<u16>(val > 0 ? 255 : 0), false));
}
for (const auto& entry : axis_list)
for (const auto& [code, name] : axis_list)
{
const u32 code = entry.first;
int val = 0;
if (libevdev_fetch_event_value(dev, EV_ABS, code, &val) == 0)
@ -298,12 +295,12 @@ std::shared_ptr<evdev_joystick_handler::EvdevDevice> evdev_joystick_handler::get
return evdev_device;
}
PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& buttons)
PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& buttons)
{
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
m_blacklist.clear();
if (first_call || get_blacklist)
if (call_type == gui_call_type::reset_input || call_type == gui_call_type::blacklist)
m_min_button_values.clear();
// Get our evdev device
@ -336,7 +333,12 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s
has_new_event |= ret == LIBEVDEV_READ_STATUS_SUCCESS;
}
auto data = GetButtonValues(device);
if (call_type == gui_call_type::get_connection)
{
return has_new_event ? connection::connected : connection::no_data;
}
const auto data = GetButtonValues(device);
const auto find_value = [&, this](const std::string& str)
{
@ -382,8 +384,8 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s
preview_values[5] = find_value(buttons[9]) - find_value(buttons[8]); // Right Stick Y
}
// return if nothing new has happened. ignore this to get the current state for blacklist or first_call
if (!get_blacklist && !first_call && !has_new_event)
// return if nothing new has happened. ignore this to get the current state for blacklist or reset_input
if (call_type != gui_call_type::blacklist && call_type != gui_call_type::reset_input && !has_new_event)
{
if (callback)
callback(0, "", padId, 0, preview_values);
@ -396,15 +398,43 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s
std::string name;
} pressed_button{};
const auto set_button_press = [&](const u32 code, const std::string& name, std::string_view type, u16 threshold, int ev_type)
const auto set_button_press = [&](const u32 code, const std::string& name, std::string_view type, u16 threshold, int ev_type, bool is_rev_axis)
{
if (!get_blacklist && m_blacklist.contains(name))
if (call_type != gui_call_type::blacklist && m_blacklist.contains(name))
return;
// Ignore codes that aren't part of the latest events. Otherwise we will get value 0 which will reset our min_value.
const auto it = data.find(static_cast<u64>(code));
if (it == data.cend())
{
if (call_type == gui_call_type::reset_input)
{
// Set to max. We won't have all the events for all the buttons or axis at this point.
m_min_button_values[name] = 65535;
if (ev_type == EV_ABS)
{
// Also set the other direction to max if it wasn't already found.
const auto it_other_axis = is_rev_axis ? axis_list.find(code) : rev_axis_list.find(code);
ensure(it_other_axis != (is_rev_axis ? axis_list.cend() : rev_axis_list.cend()));
if (const std::string& other_name = it_other_axis->second; !m_min_button_values.contains(other_name))
{
m_min_button_values[other_name] = 65535;
}
}
}
return;
}
const auto& [value, is_rev_ax] = it->second;
// If we want the value for an axis, its direction has to match the direction of the data.
if (ev_type == EV_ABS && is_rev_axis != is_rev_ax)
return;
const u16 value = data[code].first;
u16& min_value = m_min_button_values[name];
if (first_call || value < min_value)
if (call_type == gui_call_type::reset_input || value < min_value)
{
min_value = value;
return;
@ -413,7 +443,7 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s
if (value <= threshold)
return;
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
{
m_blacklist.insert(name);
@ -431,7 +461,7 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s
return;
}
const u16 diff = std::abs(min_value - value);
const u16 diff = value > min_value ? value - min_value : 0;
if (diff > button_press_threshold && value > pressed_button.value)
{
@ -453,31 +483,25 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s
if (is_sony_controller && !is_sony_guitar && (code == BTN_TL2 || code == BTN_TR2))
continue;
set_button_press(code, name, "button"sv, 0, EV_KEY);
set_button_press(code, name, "button"sv, 0, EV_KEY, false);
}
for (const auto& [code, name] : axis_list)
{
if (data[code].second)
continue;
set_button_press(code, name, "axis"sv, m_thumb_threshold, EV_ABS);
set_button_press(code, name, "axis"sv, m_thumb_threshold, EV_ABS, false);
}
for (const auto& [code, name] : rev_axis_list)
{
if (!data[code].second)
continue;
set_button_press(code, name, "rev axis"sv, m_thumb_threshold, EV_ABS);
set_button_press(code, name, "rev axis"sv, m_thumb_threshold, EV_ABS, true);
}
if (first_call)
if (call_type == gui_call_type::reset_input)
{
return connection::no_data;
}
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
{
if (m_blacklist.empty())
evdev_log.success("Evdev Calibration: Blacklist is clear. No input spam detected");

View file

@ -407,7 +407,7 @@ public:
bool Init() override;
std::vector<pad_list_entry> list_devices() override;
bool bindPadToDevice(std::shared_ptr<Pad> pad) override;
connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& buttons) override;
connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& buttons) override;
void get_motion_sensors(const std::string& padId, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array<AnalogSensor, 4>& sensors) override;
std::unordered_map<u32, std::string> get_motion_axis_list() const override;
void SetPadData(const std::string& padId, u8 player_id, u8 large_motor, u8 small_motor, s32 r, s32 g, s32 b, bool player_led, bool battery_led, u32 battery_led_brightness) override;

View file

@ -84,7 +84,7 @@ public:
void init_config(cfg_pad* cfg) override;
std::vector<pad_list_entry> list_devices() override;
connection get_next_button_press(const std::string& /*padId*/, const pad_callback& /*callback*/, const pad_fail_callback& /*fail_callback*/, bool /*first_call*/, bool /*get_blacklist*/, const std::vector<std::string>& /*buttons*/) override { return connection::connected; }
connection get_next_button_press(const std::string& /*padId*/, const pad_callback& /*callback*/, const pad_fail_callback& /*fail_callback*/, gui_call_type /*call_type*/, const std::vector<std::string>& /*buttons*/) override { return connection::connected; }
bool bindPadToDevice(std::shared_ptr<Pad> pad) override;
void process() override;

View file

@ -230,12 +230,12 @@ std::array<std::set<u32>, PadHandlerBase::button::button_count> mm_joystick_hand
return mapping;
}
PadHandlerBase::connection mm_joystick_handler::get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& buttons)
PadHandlerBase::connection mm_joystick_handler::get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& buttons)
{
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
m_blacklist.clear();
if (first_call || get_blacklist)
if (call_type == gui_call_type::reset_input || call_type == gui_call_type::blacklist)
m_min_button_values.clear();
if (!Init())
@ -281,6 +281,11 @@ PadHandlerBase::connection mm_joystick_handler::get_next_button_press(const std:
}
case JOYERR_NOERROR:
{
if (call_type == gui_call_type::get_connection)
{
return connection::connected;
}
auto data = GetButtonValues(js_info, js_caps);
// Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed.
@ -294,13 +299,13 @@ PadHandlerBase::connection mm_joystick_handler::get_next_button_press(const std:
const auto set_button_press = [&](const u64& keycode, const std::string& name, std::string_view type, u16 threshold)
{
if (!get_blacklist && m_blacklist.contains(keycode))
if (call_type != gui_call_type::blacklist && m_blacklist.contains(keycode))
return;
const u16 value = data[keycode];
u16& min_value = m_min_button_values[keycode];
if (first_call || value < min_value)
if (call_type == gui_call_type::reset_input || value < min_value)
{
min_value = value;
return;
@ -309,14 +314,14 @@ PadHandlerBase::connection mm_joystick_handler::get_next_button_press(const std:
if (value <= threshold)
return;
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
{
m_blacklist.insert(keycode);
input_log.error("MMJOY Calibration: Added %s [ %d = %s ] to blacklist. Value = %d", type, keycode, name, value);
return;
}
const u16 diff = std::abs(min_value - value);
const u16 diff = value > min_value ? value - min_value : 0;
if (diff > button_press_threshold && value > pressed_button.value)
{
@ -342,12 +347,12 @@ PadHandlerBase::connection mm_joystick_handler::get_next_button_press(const std:
set_button_press(keycode, name, "button"sv, 0);
}
if (first_call)
if (call_type == gui_call_type::reset_input)
{
return connection::no_data;
}
if (get_blacklist)
if (call_type == gui_call_type::blacklist)
{
if (m_blacklist.empty())
input_log.success("MMJOY Calibration: Blacklist is clear. No input spam detected");

View file

@ -114,7 +114,7 @@ public:
bool Init() override;
std::vector<pad_list_entry> list_devices() override;
connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& buttons) override;
connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& buttons) override;
void init_config(cfg_pad* cfg) override;
private:

View file

@ -763,14 +763,14 @@ void sdl_pad_handler::get_motion_sensors(const std::string& pad_id, const motion
PadHandlerBase::get_motion_sensors(pad_id, callback, fail_callback, preview_values, sensors);
}
PadHandlerBase::connection sdl_pad_handler::get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& buttons)
PadHandlerBase::connection sdl_pad_handler::get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& buttons)
{
if (!m_is_init)
return connection::disconnected;
SDL_PumpEvents();
return PadHandlerBase::get_next_button_press(padId, callback, fail_callback, first_call, get_blacklist, buttons);
return PadHandlerBase::get_next_button_press(padId, callback, fail_callback, call_type, buttons);
}
void sdl_pad_handler::apply_pad_data(const pad_ensemble& binding)

View file

@ -128,7 +128,7 @@ public:
void SetPadData(const std::string& padId, u8 player_id, u8 large_motor, u8 small_motor, s32 r, s32 g, s32 b, bool player_led, bool battery_led, u32 battery_led_brightness) override;
u32 get_battery_level(const std::string& padId) override;
void get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array<AnalogSensor, 4>& sensors) override;
connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, bool first_call, bool get_blacklist, const std::vector<std::string>& buttons) override;
connection get_next_button_press(const std::string& padId, const pad_callback& callback, const pad_fail_callback& fail_callback, gui_call_type call_type, const std::vector<std::string>& buttons) override;
private:
// pseudo 'controller id' to keep track of unique controllers

View file

@ -594,6 +594,7 @@ void pad_settings_dialog::InitButtons()
const u32 new_button_id = m_button_id;
const bool is_mapping = new_button_id > button_ids::id_pad_begin && new_button_id < button_ids::id_pad_end;
const bool first_call = std::exchange(button_id, new_button_id) != button_id && is_mapping;
const PadHandlerBase::gui_call_type call_type = first_call ? PadHandlerBase::gui_call_type::reset_input : PadHandlerBase::gui_call_type::normal;
const PadHandlerBase::connection status = m_handler->get_next_button_press(m_device_name,
[this, button_id](u16 val, std::string name, std::string pad_name, u32 battery_level, pad_preview_values preview_values)
@ -616,7 +617,7 @@ void pad_settings_dialog::InitButtons()
m_input_callback_data.status = PadHandlerBase::connection::disconnected;
m_input_callback_data.button_id = button_id;
},
first_call, false, buttons);
call_type, buttons);
if (status == PadHandlerBase::connection::no_data)
{
@ -642,7 +643,7 @@ void pad_settings_dialog::RefreshPads()
}
std::lock_guard lock(m_handler_mutex);
const PadHandlerBase::connection status = m_handler->get_next_button_press(info.name, nullptr, nullptr, false, false, {});
const PadHandlerBase::connection status = m_handler->get_next_button_press(info.name, nullptr, nullptr, PadHandlerBase::gui_call_type::get_connection, {});
switch_pad_info(i, info, status != PadHandlerBase::connection::disconnected);
}
}
@ -1327,7 +1328,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id)
case button_ids::id_blacklist:
{
std::lock_guard lock(m_handler_mutex);
[[maybe_unused]] const PadHandlerBase::connection status = m_handler->get_next_button_press(m_device_name, nullptr, nullptr, false, true, {});
[[maybe_unused]] const PadHandlerBase::connection status = m_handler->get_next_button_press(m_device_name, nullptr, nullptr, PadHandlerBase::gui_call_type::blacklist, {});
return;
}
default: