From 8af89b6979829ea4d20d0d3a08d829e96460de26 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Wed, 8 Aug 2018 11:50:03 +0800 Subject: [PATCH 1/2] input_common, common: Add a few functions These functions include reloading udp client, testing communication and configuring calibration. I also added a function to common/thread.h to use WaitFor. --- src/common/thread.h | 9 +++ src/input_common/main.cpp | 5 ++ src/input_common/main.h | 3 + src/input_common/udp/client.cpp | 103 +++++++++++++++++++++++++++++--- src/input_common/udp/client.h | 34 +++++++++++ src/input_common/udp/udp.cpp | 5 ++ src/input_common/udp/udp.h | 1 + 7 files changed, 153 insertions(+), 7 deletions(-) diff --git a/src/common/thread.h b/src/common/thread.h index fa475ab51..db196c05f 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -55,6 +55,15 @@ public: is_set = false; } + template + bool WaitFor(const std::chrono::duration& time) { + std::unique_lock lk(mutex); + if (!condvar.wait_for(lk, time, [this] { return is_set; })) + return false; + is_set = false; + return true; + } + template bool WaitUntil(const std::chrono::time_point& time) { std::unique_lock lk(mutex); diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index c751bdeb0..d842296d3 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -76,6 +76,11 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, return circle_pad_param.Serialize(); } +void ReloadInputDevices() { + if (udp) + udp->ReloadUDPClient(); +} + namespace Polling { std::vector> GetPollers(DeviceType type) { diff --git a/src/input_common/main.h b/src/input_common/main.h index 77a0ce90b..d1229b207 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -37,6 +37,9 @@ std::string GenerateKeyboardParam(int key_code); std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, int key_modifier, float modifier_scale); +/// Reloads the input devices +void ReloadInputDevices(); + namespace Polling { enum class DeviceType { Button, Analog }; diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 9f668d233..c0224bc6a 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -11,7 +11,6 @@ #include #include #include "common/logging/log.h" -#include "common/vector_math.h" #include "input_common/udp/client.h" #include "input_common/udp/protocol.h" @@ -128,12 +127,7 @@ static void SocketLoop(Socket* socket) { Client::Client(std::shared_ptr status, const std::string& host, u16 port, u8 pad_index, u32 client_id) : status(status) { - SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, - [this](Response::PortInfo info) { OnPortInfo(info); }, - [this](Response::PadData data) { OnPadData(data); }}; - LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); - socket = std::make_unique(host, port, pad_index, client_id, callback); - thread = std::thread{SocketLoop, this->socket.get()}; + StartCommunication(host, port, pad_index, client_id); } Client::~Client() { @@ -141,6 +135,12 @@ Client::~Client() { thread.join(); } +void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { + socket->Stop(); + thread.join(); + StartCommunication(host, port, pad_index, client_id); +} + void Client::OnVersion(Response::Version data) { LOG_TRACE(Input, "Version packet received: {}", data.version); } @@ -192,4 +192,93 @@ void Client::OnPadData(Response::PadData data) { } } +void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) { + SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, + [this](Response::PortInfo info) { OnPortInfo(info); }, + [this](Response::PadData data) { OnPadData(data); }}; + LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); + socket = std::make_unique(host, port, pad_index, client_id, callback); + thread = std::thread{SocketLoop, this->socket.get()}; +} + +void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, + std::function success_callback, + std::function failure_callback) { + std::thread([=] { + Common::Event success_event; + SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, + [&](Response::PadData data) { success_event.Set(); }}; + Socket socket{host, port, pad_index, client_id, callback}; + std::thread worker_thread{SocketLoop, &socket}; + bool result = success_event.WaitFor(std::chrono::seconds(8)); + socket.Stop(); + worker_thread.join(); + if (result) + success_callback(); + else + failure_callback(); + }) + .detach(); +} + +CalibrationConfigurationJob::CalibrationConfigurationJob( + const std::string& host, u16 port, u8 pad_index, u32 client_id, + std::function status_callback, + std::function data_callback) { + + std::thread([=] { + constexpr u16 CALIBRATION_THRESHOLD = 100; + + u16 min_x{UINT16_MAX}, min_y{UINT16_MAX}; + u16 max_x, max_y; + + Status current_status{Status::Initialized}; + SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, + [&](Response::PadData data) { + if (current_status == Status::Initialized) { + // Receiving data means the communication is ready now + current_status = Status::Ready; + status_callback(current_status); + } + if (!data.touch_1.is_active) + return; + LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, + data.touch_1.y); + min_x = std::min(min_x, static_cast(data.touch_1.x)); + min_y = std::min(min_y, static_cast(data.touch_1.y)); + if (current_status == Status::Ready) { + // First touch - min data (min_x/min_y) + current_status = Status::Stage1Completed; + status_callback(current_status); + } + if (data.touch_1.x - min_x > CALIBRATION_THRESHOLD && + data.touch_1.y - min_y > CALIBRATION_THRESHOLD) { + // Set the current position as max value and finishes + // configuration + max_x = data.touch_1.x; + max_y = data.touch_1.y; + current_status = Status::Completed; + data_callback(min_x, min_y, max_x, max_y); + status_callback(current_status); + + complete_event.Set(); + } + }}; + Socket socket{host, port, pad_index, client_id, callback}; + std::thread worker_thread{SocketLoop, &socket}; + complete_event.Wait(); + socket.Stop(); + worker_thread.join(); + }) + .detach(); +} + +CalibrationConfigurationJob::~CalibrationConfigurationJob() { + Stop(); +} + +void CalibrationConfigurationJob::Stop() { + complete_event.Set(); +} + } // namespace InputCommon::CemuhookUDP diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 0dca3b28a..a16ea34e4 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -12,6 +12,7 @@ #include #include #include "common/common_types.h" +#include "common/thread.h" #include "common/vector_math.h" namespace InputCommon::CemuhookUDP { @@ -47,15 +48,48 @@ public: explicit Client(std::shared_ptr status, const std::string& host = DEFAULT_ADDR, u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872); ~Client(); + void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0, + u32 client_id = 24872); private: void OnVersion(Response::Version); void OnPortInfo(Response::PortInfo); void OnPadData(Response::PadData); + void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id); std::unique_ptr socket; std::shared_ptr status; std::thread thread; u64 packet_sequence = 0; }; + +/// An async job allowing configuration of the touchpad calibration. +class CalibrationConfigurationJob { +public: + enum class Status { + Initialized, + Ready, + Stage1Completed, + Completed, + }; + /** + * Constructs and starts the job with the specified parameter. + * + * @param status_callback Callback for job status updates + * @param data_callback Called when calibration data is ready + */ + explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index, + u32 client_id, std::function status_callback, + std::function data_callback); + ~CalibrationConfigurationJob(); + void Stop(); + +private: + Common::Event complete_event; +}; + +void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, + std::function success_callback, + std::function failure_callback); + } // namespace InputCommon::CemuhookUDP diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 9e32d9fd9..edb3fd5ad 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -85,6 +85,11 @@ State::~State() { Input::UnregisterFactory("cemuhookudp"); } +void State::ReloadUDPClient() { + client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, + Settings::values.udp_pad_index); +} + std::unique_ptr Init() { return std::make_unique(); } diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h index 3a460c7ba..ea3de60bb 100644 --- a/src/input_common/udp/udp.h +++ b/src/input_common/udp/udp.h @@ -16,6 +16,7 @@ class State { public: State(); ~State(); + void ReloadUDPClient(); private: std::unique_ptr client; From 8db6822ee9853da92fceebecd216770a77fc4990 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sat, 11 Aug 2018 11:52:13 +0800 Subject: [PATCH 2/2] citra_qt: add motion/touch config --- src/citra_qt/CMakeLists.txt | 3 + .../configuration/configure_input.cpp | 5 + src/citra_qt/configuration/configure_input.ui | 28 ++ .../configuration/configure_motion_touch.cpp | 276 ++++++++++++++++ .../configuration/configure_motion_touch.h | 72 +++++ .../configuration/configure_motion_touch.ui | 294 ++++++++++++++++++ 6 files changed, 678 insertions(+) create mode 100644 src/citra_qt/configuration/configure_motion_touch.cpp create mode 100644 src/citra_qt/configuration/configure_motion_touch.h create mode 100644 src/citra_qt/configuration/configure_motion_touch.ui diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 571a30d7c..e1470cf94 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -39,6 +39,8 @@ add_executable(citra-qt configuration/configure_graphics.h configuration/configure_input.cpp configuration/configure_input.h + configuration/configure_motion_touch.cpp + configuration/configure_motion_touch.h configuration/configure_system.cpp configuration/configure_system.h configuration/configure_web.cpp @@ -114,6 +116,7 @@ set(UIS configuration/configure_general.ui configuration/configure_graphics.ui configuration/configure_input.ui + configuration/configure_motion_touch.ui configuration/configure_system.ui configuration/configure_web.ui debugger/registers.ui diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index bd3d664c1..510da3848 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -9,6 +9,7 @@ #include #include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_input.h" +#include "citra_qt/configuration/configure_motion_touch.h" #include "common/param_package.h" const std::array @@ -159,6 +160,10 @@ ConfigureInput::ConfigureInput(QWidget* parent) }); } + connect(ui->buttonMotionTouch, &QPushButton::released, [this] { + QDialog* motion_touch_dialog = new ConfigureMotionTouch(this); + return motion_touch_dialog->exec(); + }); connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); timeout_timer->setSingleShot(true); diff --git a/src/citra_qt/configuration/configure_input.ui b/src/citra_qt/configuration/configure_input.ui index 740fe3d29..5812a02f0 100644 --- a/src/citra_qt/configuration/configure_input.ui +++ b/src/citra_qt/configuration/configure_input.ui @@ -556,6 +556,34 @@ + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::LeftToRight + + + Motion / Touch... + + + diff --git a/src/citra_qt/configuration/configure_motion_touch.cpp b/src/citra_qt/configuration/configure_motion_touch.cpp new file mode 100644 index 000000000..b1d04f0d2 --- /dev/null +++ b/src/citra_qt/configuration/configure_motion_touch.cpp @@ -0,0 +1,276 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include "citra_qt/configuration/configure_motion_touch.h" +#include "core/settings.h" +#include "input_common/main.h" +#include "ui_configure_motion_touch.h" + +CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, + const std::string& host, u16 port, + u8 pad_index, u16 client_id) + : QDialog(parent) { + layout = new QVBoxLayout; + status_label = new QLabel(tr("Communicating with the server...")); + cancel_button = new QPushButton(tr("Cancel")); + connect(cancel_button, &QPushButton::clicked, this, [this] { + if (!completed) + job->Stop(); + accept(); + }); + layout->addWidget(status_label); + layout->addWidget(cancel_button); + setLayout(layout); + + using namespace InputCommon::CemuhookUDP; + job = std::move(std::make_unique( + host, port, pad_index, client_id, + [this](CalibrationConfigurationJob::Status status) { + QString text; + switch (status) { + case CalibrationConfigurationJob::Status::Ready: + text = tr("Touch the top left corner
of your touchpad."); + break; + case CalibrationConfigurationJob::Status::Stage1Completed: + text = tr("Now touch the bottom right corner
of your touchpad."); + break; + case CalibrationConfigurationJob::Status::Completed: + text = tr("Configuration completed!"); + break; + } + QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text)); + if (status == CalibrationConfigurationJob::Status::Completed) { + QMetaObject::invokeMethod(this, "UpdateButtonText", Q_ARG(QString, tr("OK"))); + } + }, + [this](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) { + completed = true; + min_x = min_x_; + min_y = min_y_; + max_x = max_x_; + max_y = max_y_; + })); +} + +CalibrationConfigurationDialog::~CalibrationConfigurationDialog() = default; + +void CalibrationConfigurationDialog::UpdateLabelText(QString text) { + status_label->setText(text); +} + +void CalibrationConfigurationDialog::UpdateButtonText(QString text) { + cancel_button->setText(text); +} + +const std::array, 2> MotionProviders = { + {{"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")}, + {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}}}; + +const std::array, 2> TouchProviders = { + {{"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")}, + {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")}}}; + +ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent) + : QDialog(parent), ui(std::make_unique()) { + ui->setupUi(this); + for (auto [provider, name] : MotionProviders) { + ui->motion_provider->addItem(tr(name), provider); + } + for (auto [provider, name] : TouchProviders) { + ui->touch_provider->addItem(tr(name), provider); + } + + ui->udp_learn_more->setOpenExternalLinks(true); + ui->udp_learn_more->setText( + tr("Learn More")); + + setConfiguration(); + updateUiDisplay(); + connectEvents(); +} + +ConfigureMotionTouch::~ConfigureMotionTouch() = default; + +void ConfigureMotionTouch::setConfiguration() { + Common::ParamPackage motion_param(Settings::values.motion_device); + Common::ParamPackage touch_param(Settings::values.touch_device); + std::string motion_engine = motion_param.Get("engine", "motion_emu"); + std::string touch_engine = touch_param.Get("engine", "emu_window"); + + ui->motion_provider->setCurrentIndex( + ui->motion_provider->findData(QString::fromStdString(motion_engine))); + ui->touch_provider->setCurrentIndex( + ui->touch_provider->findData(QString::fromStdString(touch_engine))); + ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f)); + + min_x = touch_param.Get("min_x", 100); + min_y = touch_param.Get("min_y", 50); + max_x = touch_param.Get("max_x", 1800); + max_y = touch_param.Get("max_y", 850); + + ui->udp_server->setText(QString::fromStdString(Settings::values.udp_input_address)); + ui->udp_port->setText(QString::number(Settings::values.udp_input_port)); + ui->udp_pad_index->setCurrentIndex(Settings::values.udp_pad_index); +} + +void ConfigureMotionTouch::updateUiDisplay() { + std::string motion_engine = ui->motion_provider->currentData().toString().toStdString(); + std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); + + if (motion_engine == "motion_emu") { + ui->motion_sensitivity_label->setVisible(true); + ui->motion_sensitivity->setVisible(true); + } else { + ui->motion_sensitivity_label->setVisible(false); + ui->motion_sensitivity->setVisible(false); + } + + if (touch_engine == "cemuhookudp") { + ui->touch_calibration->setVisible(true); + ui->touch_calibration_config->setVisible(true); + ui->touch_calibration_label->setVisible(true); + ui->touch_calibration->setText(QString("(%1, %2) - (%3, %4)") + .arg(QString::number(min_x), QString::number(min_y), + QString::number(max_x), QString::number(max_y))); + } else { + ui->touch_calibration->setVisible(false); + ui->touch_calibration_config->setVisible(false); + ui->touch_calibration_label->setVisible(false); + } + + if (motion_engine == "cemuhookudp" || touch_engine == "cemuhookudp") { + ui->udp_config_group_box->setVisible(true); + } else { + ui->udp_config_group_box->setVisible(false); + } +} + +void ConfigureMotionTouch::connectEvents() { + connect(ui->motion_provider, + static_cast(&QComboBox::currentIndexChanged), this, + [this](int index) { updateUiDisplay(); }); + connect(ui->touch_provider, + static_cast(&QComboBox::currentIndexChanged), this, + [this](int index) { updateUiDisplay(); }); + connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest); + connect(ui->touch_calibration_config, &QPushButton::clicked, this, + &ConfigureMotionTouch::OnConfigureTouchCalibration); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { + if (CanCloseDialog()) + reject(); + }); +} + +void ConfigureMotionTouch::OnCemuhookUDPTest() { + ui->udp_test->setEnabled(false); + ui->udp_test->setText(tr("Testing")); + udp_test_in_progress = true; + InputCommon::CemuhookUDP::TestCommunication( + ui->udp_server->text().toStdString(), static_cast(ui->udp_port->text().toInt()), + static_cast(ui->udp_pad_index->currentIndex()), 24872, + [this] { + LOG_INFO(Frontend, "UDP input test success"); + QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); + }, + [this] { + LOG_ERROR(Frontend, "UDP input test failed"); + QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, false)); + }); +} + +void ConfigureMotionTouch::OnConfigureTouchCalibration() { + ui->touch_calibration_config->setEnabled(false); + ui->touch_calibration_config->setText(tr("Configuring")); + CalibrationConfigurationDialog* dialog = new CalibrationConfigurationDialog( + this, ui->udp_server->text().toStdString(), static_cast(ui->udp_port->text().toUInt()), + static_cast(ui->udp_pad_index->currentIndex()), 24872); + dialog->exec(); + if (dialog->completed) { + min_x = dialog->min_x; + min_y = dialog->min_y; + max_x = dialog->max_x; + max_y = dialog->max_y; + LOG_INFO(Frontend, + "UDP touchpad calibration config success: min_x={}, min_y={}, max_x={}, max_y={}", + min_x, min_y, max_x, max_y); + updateUiDisplay(); + } else { + LOG_ERROR(Frontend, "UDP touchpad calibration config failed"); + } + ui->touch_calibration_config->setEnabled(true); + ui->touch_calibration_config->setText(tr("Configure")); +} + +void ConfigureMotionTouch::closeEvent(QCloseEvent* event) { + if (CanCloseDialog()) + event->accept(); + else + event->ignore(); +} + +void ConfigureMotionTouch::ShowUDPTestResult(bool result) { + udp_test_in_progress = false; + if (result) { + QMessageBox::information(this, tr("Test Successful"), + tr("Successfully received data from the server.")); + } else { + QMessageBox::warning(this, tr("Test Failed"), + tr("Could not receive valid data from the server.
Please verify " + "that the server is set up correctly and " + "the address and port are correct.")); + } + ui->udp_test->setEnabled(true); + ui->udp_test->setText(tr("Test")); +} + +bool ConfigureMotionTouch::CanCloseDialog() { + if (udp_test_in_progress) { + QMessageBox::warning(this, tr("Citra"), + tr("UDP Test or calibration configuration is in progress.
Please " + "wait for them to finish.")); + return false; + } + return true; +} + +void ConfigureMotionTouch::applyConfiguration() { + if (!CanCloseDialog()) + return; + + std::string motion_engine = ui->motion_provider->currentData().toString().toStdString(); + std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); + + Common::ParamPackage motion_param{}, touch_param{}; + motion_param.Set("engine", motion_engine); + touch_param.Set("engine", touch_engine); + + if (motion_engine == "motion_emu") { + motion_param.Set("sensitivity", static_cast(ui->motion_sensitivity->value())); + } + + if (touch_engine == "cemuhookudp") { + touch_param.Set("min_x", min_x); + touch_param.Set("min_y", min_y); + touch_param.Set("max_x", max_x); + touch_param.Set("max_y", max_y); + } + + Settings::values.motion_device = motion_param.Serialize(); + Settings::values.touch_device = touch_param.Serialize(); + Settings::values.udp_input_address = ui->udp_server->text().toStdString(); + Settings::values.udp_input_port = static_cast(ui->udp_port->text().toInt()); + Settings::values.udp_pad_index = static_cast(ui->udp_pad_index->currentIndex()); + InputCommon::ReloadInputDevices(); + + accept(); +} diff --git a/src/citra_qt/configuration/configure_motion_touch.h b/src/citra_qt/configuration/configure_motion_touch.h new file mode 100644 index 000000000..a7662b103 --- /dev/null +++ b/src/citra_qt/configuration/configure_motion_touch.h @@ -0,0 +1,72 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/param_package.h" +#include "input_common/udp/udp.h" + +class QVBoxLayout; +class QLabel; +class QPushButton; + +namespace Ui { +class ConfigureMotionTouch; +} + +/// A dialog for touchpad calibration configuration. +class CalibrationConfigurationDialog : public QDialog { + Q_OBJECT +public: + explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port, + u8 pad_index, u16 client_id); + ~CalibrationConfigurationDialog(); + +private: + Q_INVOKABLE void UpdateLabelText(QString text); + Q_INVOKABLE void UpdateButtonText(QString text); + + QVBoxLayout* layout; + QLabel* status_label; + QPushButton* cancel_button; + std::unique_ptr job; + + // Configuration results + bool completed{}; + u16 min_x, min_y, max_x, max_y; + + friend class ConfigureMotionTouch; +}; + +class ConfigureMotionTouch : public QDialog { + Q_OBJECT + +public: + explicit ConfigureMotionTouch(QWidget* parent = nullptr); + ~ConfigureMotionTouch(); + +public slots: + void applyConfiguration(); + +private slots: + void OnCemuhookUDPTest(); + void OnConfigureTouchCalibration(); + +private: + void closeEvent(QCloseEvent* event) override; + Q_INVOKABLE void ShowUDPTestResult(bool result); + void setConfiguration(); + void updateUiDisplay(); + void connectEvents(); + bool CanCloseDialog(); + + std::unique_ptr ui; + + // Coordinate system of the CemuhookUDP touch provider + int min_x, min_y, max_x, max_y; + + bool udp_test_in_progress{}; +}; diff --git a/src/citra_qt/configuration/configure_motion_touch.ui b/src/citra_qt/configuration/configure_motion_touch.ui new file mode 100644 index 000000000..17bf3481f --- /dev/null +++ b/src/citra_qt/configuration/configure_motion_touch.ui @@ -0,0 +1,294 @@ + + + ConfigureMotionTouch + + + Configure Motion / Touch + + + + 0 + 0 + 500 + 450 + + + + + + + Motion + + + + + + + + Motion Provider: + + + + + + + + + + + + + + Sensitivity: + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 4 + + + 0.010000000000000 + + + 10.000000000000000 + + + 0.001000000000000 + + + 0.010000000000000 + + + + + + + + + + + + Touch + + + + + + + + Touch Provider: + + + + + + + + + + + + + + Calibration: + + + + + + + (100, 50) - (1800, 850) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Configure + + + + + + + + + + + + CemuhookUDP Config + + + + + + You may use any Cemuhook compatible UDP input source to provide motion and touch input. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + Server: + + + + + + + + 0 + 0 + + + + + + + + + + + + Port: + + + + + + + + 0 + 0 + + + + + + + + + + + + Pad: + + + + + + + + Pad 1 + + + + + Pad 2 + + + + + Pad 3 + + + + + Pad 4 + + + + + + + + + + + + Learn More + + + + + + + + 0 + 0 + + + + Test + + + + + + + + + + + + Qt::Vertical + + + + 167 + 55 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ConfigureMotionTouch + applyConfiguration() + + + 220 + 380 + + + 220 + 200 + + + + +