rpcsx/rpcs3/rpcs3qt/pad_motion_settings_dialog.cpp
2023-06-15 19:48:15 +02:00

289 lines
8.2 KiB
C++

#include "stdafx.h"
#include "pad_motion_settings_dialog.h"
#include <QComboBox>
#include <thread>
LOG_CHANNEL(cfg_log, "CFG");
pad_motion_settings_dialog::pad_motion_settings_dialog(QDialog* parent, std::shared_ptr<PadHandlerBase> handler, cfg_player* cfg)
: QDialog(parent)
, ui(new Ui::pad_motion_settings_dialog)
, m_handler(handler)
, m_cfg(cfg)
{
ui->setupUi(this);
setModal(true);
ensure(m_handler);
ensure(m_cfg);
cfg_pad& pad = m_cfg->config;
m_preview_sliders = {{ ui->slider_x, ui->slider_y, ui->slider_z, ui->slider_g }};
m_preview_labels = {{ ui->label_x, ui->label_y, ui->label_z, ui->label_g }};
m_axis_names = {{ ui->combo_x, ui->combo_y, ui->combo_z, ui->combo_g }};
m_mirrors = {{ ui->mirror_x, ui->mirror_y, ui->mirror_z, ui->mirror_g }};
m_shifts = {{ ui->shift_x, ui->shift_y, ui->shift_z, ui->shift_g }};
m_config_entries = {{ &pad.motion_sensor_x, &pad.motion_sensor_y, &pad.motion_sensor_z, &pad.motion_sensor_g }};
for (usz i = 0; i < m_preview_sliders.size(); i++)
{
m_preview_sliders[i]->setRange(0, 1023);
m_preview_labels[i]->setText("0");
}
#if HAVE_LIBEVDEV
const bool has_device_list = m_handler->m_type == pad_handler::evdev;
#else
const bool has_device_list = false;
#endif
if (has_device_list)
{
// Combobox: Motion Devices
m_device_name = m_cfg->buddy_device.to_string();
ui->cb_choose_device->addItem(tr("Disabled"), QVariant::fromValue(pad_device_info{}));
const std::vector<pad_list_entry> device_list = m_handler->list_devices();
for (const pad_list_entry& device : device_list)
{
if (device.is_buddy_only)
{
const QString device_name = QString::fromStdString(device.name);
const QVariant user_data = QVariant::fromValue(pad_device_info{ device.name, true });
ui->cb_choose_device->addItem(device_name, user_data);
}
}
for (int i = 0; i < ui->cb_choose_device->count(); i++)
{
const QVariant user_data = ui->cb_choose_device->itemData(i);
ensure(user_data.canConvert<pad_device_info>());
if (const pad_device_info info = user_data.value<pad_device_info>(); info.name == m_device_name)
{
ui->cb_choose_device->setCurrentIndex(i);
break;
}
}
connect(ui->cb_choose_device, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &pad_motion_settings_dialog::change_device);
// Combobox: Configure Axis
m_motion_axis_list = m_handler->get_motion_axis_list();
for (const auto& [code, axis] : m_motion_axis_list)
{
const QString q_axis = QString::fromStdString(axis);
for (usz i = 0; i < m_axis_names.size(); i++)
{
m_axis_names[i]->addItem(q_axis, code);
if (m_config_entries[i]->axis.to_string() == axis)
{
m_axis_names[i]->setCurrentIndex(m_axis_names[i]->findData(code));
}
}
}
for (usz i = 0; i < m_axis_names.size(); i++)
{
const cfg_sensor* config = m_config_entries[i];
m_mirrors[i]->setChecked(config->mirrored.get());
m_shifts[i]->setRange(config->shift.min, config->shift.max);
m_shifts[i]->setValue(config->shift.get());
connect(m_mirrors[i], &QCheckBox::stateChanged, this, [this, i](int state)
{
std::lock_guard lock(m_config_mutex);
m_config_entries[i]->mirrored.set(state != Qt::Unchecked);
});
connect(m_shifts[i], QOverload<int>::of(&QSpinBox::valueChanged), this, [this, i](int value)
{
std::lock_guard lock(m_config_mutex);
m_config_entries[i]->shift.set(value);
});
connect(m_axis_names[i], QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this, i](int index)
{
std::lock_guard lock(m_config_mutex);
if (!m_config_entries[i]->axis.from_string(m_axis_names[i]->itemText(index).toStdString()))
{
cfg_log.error("Failed to convert motion axis string: %s", m_axis_names[i]->itemData(index).toString());
}
});
}
}
else
{
m_device_name = m_cfg->device.to_string();
ui->gb_device->setVisible(false);
ui->cancelButton->setVisible(false);
for (usz i = 0; i < m_axis_names.size(); i++)
{
m_axis_names[i]->setVisible(false);
m_mirrors[i]->setVisible(false);
m_shifts[i]->setVisible(false);
}
}
// Use timer to display button input
connect(&m_timer_input, &QTimer::timeout, this, [this]()
{
motion_callback_data data;
{
std::lock_guard lock(m_input_mutex);
data = m_motion_callback_data;
m_motion_callback_data.has_new_data = false;
}
if (data.has_new_data)
{
// Starting with 1 because the first entry is the Disabled entry.
for (int i = 1; i < ui->cb_choose_device->count(); i++)
{
const QVariant user_data = ui->cb_choose_device->itemData(i);
ensure(user_data.canConvert<pad_device_info>());
if (const pad_device_info info = user_data.value<pad_device_info>(); info.name == data.pad_name)
{
switch_buddy_pad_info(i, info, data.success);
break;
}
}
for (usz i = 0; i < data.preview_values.size(); i++)
{
m_preview_sliders[i]->setValue(data.preview_values[i]);
m_preview_labels[i]->setText(QString::number(data.preview_values[i]));
}
}
});
m_timer_input.start(1);
// Use thread to get button input
m_input_thread = std::make_unique<named_thread<std::function<void()>>>("UI Pad Motion Thread", [this]()
{
while (thread_ctrl::state() != thread_state::aborting)
{
thread_ctrl::wait_for(1000);
if (m_input_thread_state != input_thread_state::active)
{
if (m_input_thread_state == input_thread_state::pausing)
{
m_input_thread_state = input_thread_state::paused;
}
continue;
}
std::array<AnalogSensor, 4> sensors{};
{
std::lock_guard lock(m_config_mutex);
for (usz i = 0; i < sensors.size(); i++)
{
AnalogSensor& sensor = sensors[i];
const cfg_sensor* config = m_config_entries[i];
const std::string cfgname = config->axis.to_string();
for (const auto& [code, name] : m_motion_axis_list)
{
if (cfgname == name)
{
sensor.m_keyCode = code;
sensor.m_mirrored = config->mirrored.get();
sensor.m_shift = config->shift.get();
break;
}
}
}
}
m_handler->get_motion_sensors(m_device_name,
[this](std::string pad_name, motion_preview_values preview_values)
{
std::lock_guard lock(m_input_mutex);
m_motion_callback_data.pad_name = std::move(pad_name);
m_motion_callback_data.preview_values = std::move(preview_values);
m_motion_callback_data.has_new_data = true;
m_motion_callback_data.success = true;
},
[this](std::string pad_name, motion_preview_values preview_values)
{
std::lock_guard lock(m_input_mutex);
m_motion_callback_data.pad_name = std::move(pad_name);
m_motion_callback_data.preview_values = std::move(preview_values);
m_motion_callback_data.has_new_data = true;
m_motion_callback_data.success = false;
},
m_motion_callback_data.preview_values, sensors);
}
});
start_input_thread();
}
pad_motion_settings_dialog::~pad_motion_settings_dialog()
{
if (m_input_thread)
{
m_input_thread_state = input_thread_state::pausing;
auto& thread = *m_input_thread;
thread = thread_state::aborting;
thread();
}
}
void pad_motion_settings_dialog::change_device(int index)
{
if (index < 0)
return;
const QVariant user_data = ui->cb_choose_device->itemData(index);
ensure(user_data.canConvert<pad_device_info>());
const pad_device_info info = user_data.value<pad_device_info>();
if (!m_cfg->buddy_device.from_string(info.name))
{
cfg_log.error("Failed to convert motion device string: %s", info.name);
}
m_device_name = m_cfg->buddy_device.to_string();
}
void pad_motion_settings_dialog::switch_buddy_pad_info(int index, pad_device_info info, bool is_connected)
{
if (index >= 0 && info.is_connected != is_connected)
{
info.is_connected = is_connected;
ui->cb_choose_device->setItemData(index, QVariant::fromValue(info));
ui->cb_choose_device->setItemText(index, is_connected ? QString::fromStdString(info.name) : (QString::fromStdString(info.name) + Disconnected_suffix));
}
}
void pad_motion_settings_dialog::start_input_thread()
{
m_input_thread_state = input_thread_state::active;
}
void pad_motion_settings_dialog::pause_input_thread()
{
if (m_input_thread)
{
m_input_thread_state = input_thread_state::pausing;
while (m_input_thread_state != input_thread_state::paused)
{
std::this_thread::sleep_for(1ms);
}
}
}