This commit is contained in:
David Griswold 2025-10-03 16:25:56 -04:00 committed by GitHub
commit 06d9af449c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 183 additions and 11 deletions

View file

@ -403,8 +403,14 @@ void QtConfig::ReadControlValues() {
ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window"))
.toString()
.toStdString();
profile.controller_touch_device =
ReadSetting(QStringLiteral("controller_touch_device"),QStringLiteral(""))
.toString()
.toStdString();
profile.use_touch_from_button =
ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool();
profile.use_touchpad =
ReadSetting(QStringLiteral("use_touchpad"), false).toBool();
profile.touch_from_button_map_index =
ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt();
profile.touch_from_button_map_index =
@ -988,6 +994,8 @@ void QtConfig::SaveControlValues() {
WriteSetting(QStringLiteral("touch_device"), QString::fromStdString(profile.touch_device),
QStringLiteral("engine:emu_window"));
WriteSetting(QStringLiteral("use_touch_from_button"), profile.use_touch_from_button, false);
WriteSetting(QStringLiteral("use_touchpad"), profile.use_touchpad, false);
WriteSetting(QStringLiteral("controller_touch_device"), QString::fromStdString(profile.controller_touch_device), QStringLiteral(""));
WriteSetting(QStringLiteral("touch_from_button_map"), profile.touch_from_button_map_index,
0);
WriteSetting(QStringLiteral("udp_input_address"),

View file

@ -139,6 +139,8 @@ void ConfigureMotionTouch::SetConfiguration() {
ui->touch_provider->findData(QString::fromStdString(touch_engine)));
ui->touch_from_button_checkbox->setChecked(
Settings::values.current_input_profile.use_touch_from_button);
ui->touchpad_checkbox->setChecked(
Settings::values.current_input_profile.use_touchpad);
touch_from_button_maps = Settings::values.touch_from_button_maps;
for (const auto& touch_map : touch_from_button_maps) {
ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
@ -164,7 +166,9 @@ void ConfigureMotionTouch::SetConfiguration() {
void ConfigureMotionTouch::UpdateUiDisplay() {
const std::string motion_engine = ui->motion_provider->currentData().toString().toStdString();
const std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
ui->touchpad_config_btn->setEnabled(
ui->touchpad_checkbox->isChecked()
);
if (motion_engine == "motion_emu") {
ui->motion_sensitivity_label->setVisible(true);
ui->motion_sensitivity->setVisible(true);
@ -229,6 +233,32 @@ void ConfigureMotionTouch::ConnectEvents() {
poll_timer->start(200); // Check for new inputs every 200ms
}
});
connect(ui->touchpad_checkbox,&QCheckBox::checkStateChanged, this, [this]() {UpdateUiDisplay(); });
connect(ui->touchpad_config_btn, &QPushButton::clicked, this, [this]() {
if (QMessageBox::information(this, tr("Information"),
tr("After pressing OK, tap the trackpad on the controller"
"that you want to track."),
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
ui->touchpad_config_btn->setText(tr("[press touchpad]"));
ui->touchpad_config_btn->setFocus();
input_setter = [this](const Common::ParamPackage& params) {
tpguid = params.Get("guid", "0");
tpport = params.Get("port", 0);
tp = params.Get("touchpad",0);
};
device_pollers =
InputCommon::Polling::GetPollers(InputCommon::Polling::DeviceType::Touchpad);
for (auto& poller : device_pollers) {
poller->Start();
}
timeout_timer->start(5000); // Cancel after 5 seconds
poll_timer->start(200); // Check for new inputs every 200ms
}
});
connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest);
connect(ui->touch_calibration_config, &QPushButton::clicked, this,
&ConfigureMotionTouch::OnConfigureTouchCalibration);
@ -253,7 +283,7 @@ void ConfigureMotionTouch::SetPollingResult(const Common::ParamPackage& params,
if (!abort && input_setter) {
(*input_setter)(params);
}
ui->touchpad_config_btn->setText(tr("Configure"));
ui->motion_controller_button->setText(tr("Configure"));
input_setter.reset();
}
@ -291,7 +321,6 @@ void ConfigureMotionTouch::OnConfigureTouchCalibration() {
"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);
@ -374,12 +403,22 @@ void ConfigureMotionTouch::ApplyConfiguration() {
touch_param.Set("max_y", max_y);
}
Common::ParamPackage touchpad_param{};
if (ui->touchpad_checkbox->isChecked()) {
touchpad_param.Set("engine", "sdl");
touchpad_param.Set("guid", tpguid);
touchpad_param.Set("port", tpport);
touchpad_param.Set("touchpad",tp);
}
Settings::values.current_input_profile.motion_device = motion_param.Serialize();
Settings::values.current_input_profile.touch_device = touch_param.Serialize();
Settings::values.current_input_profile.use_touch_from_button =
ui->touch_from_button_checkbox->isChecked();
Settings::values.current_input_profile.touch_from_button_map_index =
ui->touch_from_button_map->currentIndex();
Settings::values.current_input_profile.use_touchpad =
ui->touchpad_checkbox->isChecked();
Settings::values.current_input_profile.controller_touch_device = touchpad_param.Serialize();
Settings::values.touch_from_button_maps = touch_from_button_maps;
Settings::values.current_input_profile.udp_input_address = ui->udp_server->text().toStdString();
Settings::values.current_input_profile.udp_input_port =

View file

@ -76,6 +76,9 @@ private:
// Used for SDL input polling
std::string guid;
int port;
std::string tpguid; // guid for touchpad
int tpport; //port for touchpad
int tp; //which touchpad
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;

View file

@ -2,17 +2,17 @@
<ui version="4.0">
<class>ConfigureMotionTouch</class>
<widget class="QDialog" name="ConfigureMotionTouch">
<property name="windowTitle">
<string>Configure Motion / Touch</string>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>450</height>
<width>517</width>
<height>659</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Motion / Touch</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox" name="motion_group_box">
@ -175,6 +175,24 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="touchpad_hlayout">
<item>
<widget class="QCheckBox" name="touchpad_checkbox">
<property name="text">
<string>Use controller (e.g. DualSense) touchpad</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignRight">
<widget class="QPushButton" name="touchpad_config_btn">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -324,4 +342,5 @@
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -121,6 +121,8 @@ void SdlConfig::ReadValues() {
"engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0");
Settings::values.current_input_profile.touch_device =
sdl2_config->GetString("Controls", "touch_device", "engine:emu_window");
Settings::values.current_input_profile.controller_touch_device =
sdl2_config->GetString("Controls","controller_touch_device","");
Settings::values.current_input_profile.udp_input_address = sdl2_config->GetString(
"Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_ADDR);
Settings::values.current_input_profile.udp_input_port =

View file

@ -431,6 +431,8 @@ struct InputProfile {
std::array<std::string, NativeAnalog::NumAnalogs> analogs;
std::string motion_device;
std::string touch_device;
std::string controller_touch_device;
bool use_touchpad;
bool use_touch_from_button;
int touch_from_button_map_index;
std::string udp_input_address;

View file

@ -115,6 +115,7 @@ DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
}
void Module::LoadInputDevices() {
LOG_DEBUG(Frontend,"Loading input devices");
std::transform(Settings::values.current_input_profile.buttons.begin() +
Settings::NativeButton::BUTTON_HID_BEGIN,
Settings::values.current_input_profile.buttons.begin() +
@ -126,6 +127,12 @@ void Module::LoadInputDevices() {
Settings::values.current_input_profile.motion_device);
touch_device = Input::CreateDevice<Input::TouchDevice>(
Settings::values.current_input_profile.touch_device);
if (Settings::values.current_input_profile.use_touchpad &&
Settings::values.current_input_profile.controller_touch_device != "") {
controller_touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.current_input_profile.controller_touch_device);
} else {
controller_touch_device.reset();
}
if (Settings::values.current_input_profile.use_touch_from_button) {
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
} else {
@ -278,6 +285,9 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) {
if (!pressed && touch_btn_device) {
std::tie(x, y, pressed) = touch_btn_device->GetStatus();
}
if (!pressed && controller_touch_device) {
std::tie(x,y,pressed) = controller_touch_device->GetStatus();
}
touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth);
touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight);
touch_entry.valid.Assign(pressed ? 1 : 0);

View file

@ -390,6 +390,7 @@ private:
buttons;
std::unique_ptr<Input::AnalogDevice> circle_pad;
std::unique_ptr<Input::MotionDevice> motion_device;
std::unique_ptr<Input::TouchDevice> controller_touch_device;
std::unique_ptr<Input::TouchDevice> touch_device;
std::unique_ptr<Input::TouchDevice> touch_btn_device;

View file

@ -45,7 +45,7 @@ void ReloadInputDevices();
namespace Polling {
enum class DeviceType { Button, Analog };
enum class DeviceType { Button, Analog, Touchpad};
/**
* A class that can be used to get inputs from an input device like controllers without having to

View file

@ -182,6 +182,11 @@ public:
return has_gyro || has_accel;
}
void SetTouchpad(float x, float y, int touchpad, bool down) {
std::lock_guard lock{mutex};
state.touchpad[touchpad] = std::make_tuple(x, y, down);
}
void SetButton(int button, bool value) {
std::lock_guard lock{mutex};
state.buttons[button] = value;
@ -246,6 +251,11 @@ public:
return std::make_tuple(state.accel, state.gyro);
}
std::tuple<float, float, bool> GetTouch(int pad) {
std::lock_guard lock{mutex};
return state.touchpad[pad];
}
/**
* The guid of the joystick
*/
@ -280,6 +290,7 @@ private:
std::unordered_map<int, Uint8> hats;
Common::Vec3<float> accel;
Common::Vec3<float> gyro;
std::unordered_map<int, std::tuple<float, float, bool>> touchpad;
} state;
std::string guid;
int port;
@ -590,6 +601,17 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
}
break;
}
case SDL_CONTROLLERTOUCHPADDOWN:
case SDL_CONTROLLERTOUCHPADMOTION:
if (auto joystick = GetSDLJoystickBySDLID(event.ctouchpad.which)) {
joystick->SetTouchpad(event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.touchpad, true);
}
break;
case SDL_CONTROLLERTOUCHPADUP:
if (auto joystick = GetSDLJoystickBySDLID(event.ctouchpad.which)) {
joystick->SetTouchpad(event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.touchpad, false);
}
break;
#endif
case SDL_JOYDEVICEREMOVED:
LOG_DEBUG(Input, "Joystick removed with Instance_ID {}", event.jdevice.which);
@ -691,6 +713,19 @@ private:
std::shared_ptr<SDLJoystick> joystick;
};
class SDLTouch final : public Input::TouchDevice {
public:
explicit SDLTouch(std::shared_ptr<SDLJoystick> joystick_, int pad_) :
joystick(std::move(joystick_)),pad(pad_) {}
std::tuple<float, float, bool> GetStatus() const override {
return joystick->GetTouch(pad);
}
private:
std::shared_ptr<SDLJoystick> joystick;
const int pad;
};
/// A button device factory that creates button devices from SDL joystick
class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
public:
@ -810,6 +845,30 @@ public:
return std::make_unique<SDLMotion>(joystick);
}
private:
SDLState& state;
};
/**
* A factory that creates a TouchDevice from an SDL Touchpad
*/
class SDLTouchFactory final : public Input::Factory<Input::TouchDevice> {
public:
explicit SDLTouchFactory(SDLState& state_) : state(state_) {}
/**
* Creates touch device from touchpad
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
* - "touchpad": which touchpad to bind
*/
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
const int touchpad = params.Get("touchpad", 0);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
return std::make_unique<SDLTouch>(joystick, touchpad);
}
private:
SDLState& state;
};
@ -819,7 +878,7 @@ SDLState::SDLState() {
RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
RegisterFactory<MotionDevice>("sdl", std::make_shared<SDLMotionFactory>(*this));
RegisterFactory<TouchDevice>("sdl",std::make_shared<SDLTouchFactory>(*this));
// If the frontend is going to manage the event loop, then we dont start one here
start_thread = !SDL_WasInit(SDL_INIT_GAMECONTROLLER);
if (start_thread && SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) {
@ -874,7 +933,7 @@ SDLState::~SDLState() {
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
UnregisterFactory<MotionDevice>("sdl");
UnregisterFactory<TouchDevice>("sdl");
CloseJoysticks();
SDL_DelEventWatch(&SDLEventWatcher, this);
@ -956,6 +1015,30 @@ protected:
SDLState& state;
};
class SDLTouchpadPoller final: public SDLPoller {
public:
explicit SDLTouchpadPoller(SDLState& state_) : SDLPoller(state_) {}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
Common::ParamPackage params;
while (state.event_queue.Pop(event)) {
if (event.type != SDL_CONTROLLERTOUCHPADDOWN) {
continue;
}
switch (event.type) {
case SDL_CONTROLLERTOUCHPADDOWN:
auto joystick = state.GetSDLJoystickBySDLID(event.ctouchpad.which);
params.Set("engine", "sdl");
params.Set("touchpad",event.ctouchpad.touchpad);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
}
}
return params;
}
};
class SDLButtonPoller final : public SDLPoller {
public:
explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
@ -1083,6 +1166,9 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
case InputCommon::Polling::DeviceType::Button:
pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
break;
case InputCommon::Polling::DeviceType::Touchpad:
pollers.emplace_back(std::make_unique<Polling::SDLTouchpadPoller>(*this));
break;
}
return pollers;

View file

@ -24,6 +24,7 @@ class SDLGameController;
class SDLButtonFactory;
class SDLAnalogFactory;
class SDLMotionFactory;
class SDLTouchFactory;
class SDLState : public State {
public:
@ -62,6 +63,7 @@ private:
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
std::shared_ptr<SDLTouchFactory> touch_factory;
std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory;
std::shared_ptr<SDLMotionFactory> motion_factory;