diff --git a/CMakeModules/GenerateSettingKeys.cmake b/CMakeModules/GenerateSettingKeys.cmake index 15a28a5de..4fe58113c 100644 --- a/CMakeModules/GenerateSettingKeys.cmake +++ b/CMakeModules/GenerateSettingKeys.cmake @@ -199,6 +199,8 @@ if (ENABLE_QT) "name" "bind" "profile" + "use_touchpad" + "controller_touch_device" "use_touch_from_button" "touch_from_button_map" "touch_from_button_maps" # Why are these two so similar? Basically typo bait diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index fa6ff311d..a5aa9896b 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -405,6 +405,11 @@ void QtConfig::ReadControlValues() { ReadSetting(Settings::QKeys::touch_device, QStringLiteral("engine:emu_window")) .toString() .toStdString(); + profile.use_touchpad = ReadSetting(Settings::QKeys::use_touchpad, false).toBool(); + profile.controller_touch_device = + ReadSetting(Settings::QKeys::controller_touch_device, QStringLiteral("")) + .toString() + .toStdString(); profile.use_touch_from_button = ReadSetting(Settings::QKeys::use_touch_from_button, false).toBool(); profile.touch_from_button_map_index = @@ -1005,6 +1010,9 @@ void QtConfig::SaveControlValues() { QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0")); WriteSetting(Settings::QKeys::touch_device, QString::fromStdString(profile.touch_device), QStringLiteral("engine:emu_window")); + WriteSetting(Settings::QKeys::use_touchpad, profile.use_touchpad, false); + WriteSetting(Settings::QKeys::controller_touch_device, + QString::fromStdString(profile.controller_touch_device), QStringLiteral("")); WriteSetting(Settings::QKeys::use_touch_from_button, profile.use_touch_from_button, false); WriteSetting(Settings::QKeys::touch_from_button_map, profile.touch_from_button_map_index, 0); diff --git a/src/citra_qt/configuration/configure_motion_touch.cpp b/src/citra_qt/configuration/configure_motion_touch.cpp index 24e05dad3..5b9b79bd7 100644 --- a/src/citra_qt/configuration/configure_motion_touch.cpp +++ b/src/citra_qt/configuration/configure_motion_touch.cpp @@ -139,6 +139,7 @@ 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 +165,7 @@ 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 +230,33 @@ 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 touchpad on the controller " + "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 +281,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 +319,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 +401,21 @@ 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 = diff --git a/src/citra_qt/configuration/configure_motion_touch.h b/src/citra_qt/configuration/configure_motion_touch.h index 3b10f752a..f399eefbe 100644 --- a/src/citra_qt/configuration/configure_motion_touch.h +++ b/src/citra_qt/configuration/configure_motion_touch.h @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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 timeout_timer; std::unique_ptr poll_timer; std::vector> device_pollers; diff --git a/src/citra_qt/configuration/configure_motion_touch.ui b/src/citra_qt/configuration/configure_motion_touch.ui index 7f4a5c36a..e10367259 100644 --- a/src/citra_qt/configuration/configure_motion_touch.ui +++ b/src/citra_qt/configuration/configure_motion_touch.ui @@ -2,17 +2,17 @@ ConfigureMotionTouch - - Configure Motion / Touch - 0 0 - 500 - 450 + 517 + 659 + + Configure Motion / Touch + @@ -175,6 +175,27 @@ + + + + + + Map touchpads on controllers like the DualSense directly to touch + + + Use controller touchpad + + + + + + + Configure + + + + + @@ -324,4 +345,5 @@ + diff --git a/src/common/settings.h b/src/common/settings.h index e03c46961..90922bcff 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -442,6 +442,8 @@ struct InputProfile { std::array 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; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 81b833f34..22fbec8cb 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -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,13 @@ void Module::LoadInputDevices() { Settings::values.current_input_profile.motion_device); touch_device = Input::CreateDevice( 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( + 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("engine:touch_from_button"); } else { @@ -278,6 +286,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(x * Core::kScreenBottomWidth); touch_entry.y = static_cast(y * Core::kScreenBottomHeight); touch_entry.valid.Assign(pressed ? 1 : 0); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 79713ca69..33250e18b 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -1,4 +1,4 @@ -// Copyright 2015 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -390,6 +390,7 @@ private: buttons; std::unique_ptr circle_pad; std::unique_ptr motion_device; + std::unique_ptr controller_touch_device; std::unique_ptr touch_device; std::unique_ptr touch_btn_device; diff --git a/src/input_common/main.h b/src/input_common/main.h index 48ecd26ba..bbc7669e7 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -1,4 +1,4 @@ -// Copyright 2017 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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 diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp index 644db3448..0c6cc8ddb 100644 --- a/src/input_common/sdl/sdl.cpp +++ b/src/input_common/sdl/sdl.cpp @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 1833e49cb..71f2be379 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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 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 hats; Common::Vec3 accel; Common::Vec3 gyro; + std::unordered_map> touchpad; } state; std::string guid; int port; @@ -590,6 +601,19 @@ 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 +715,20 @@ private: std::shared_ptr joystick; }; +class SDLTouch final : public Input::TouchDevice { +public: + explicit SDLTouch(std::shared_ptr joystick_, int pad_) + : joystick(std::move(joystick_)), pad(pad_) {} + + std::tuple GetStatus() const override { + return joystick->GetTouch(pad); + } + +private: + std::shared_ptr joystick; + const int pad; +}; + /// A button device factory that creates button devices from SDL joystick class SDLButtonFactory final : public Input::Factory { public: @@ -810,6 +848,30 @@ public: return std::make_unique(joystick); } +private: + SDLState& state; +}; +/** + * A factory that creates a TouchDevice from an SDL Touchpad + */ +class SDLTouchFactory final : public Input::Factory { +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 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(joystick, touchpad); + } + private: SDLState& state; }; @@ -819,7 +881,7 @@ SDLState::SDLState() { RegisterFactory("sdl", std::make_shared(*this)); RegisterFactory("sdl", std::make_shared(*this)); RegisterFactory("sdl", std::make_shared(*this)); - + RegisterFactory("sdl", std::make_shared(*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 +936,7 @@ SDLState::~SDLState() { UnregisterFactory("sdl"); UnregisterFactory("sdl"); UnregisterFactory("sdl"); - + UnregisterFactory("sdl"); CloseJoysticks(); SDL_DelEventWatch(&SDLEventWatcher, this); @@ -956,6 +1018,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 +1169,9 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) { case InputCommon::Polling::DeviceType::Button: pollers.emplace_back(std::make_unique(*this)); break; + case InputCommon::Polling::DeviceType::Touchpad: + pollers.emplace_back(std::make_unique(*this)); + break; } return pollers; diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 56f53121b..4106572d9 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -1,4 +1,4 @@ -// Copyright 2018 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -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>> joystick_map; std::mutex joystick_map_mutex; + std::shared_ptr touch_factory; std::shared_ptr button_factory; std::shared_ptr analog_factory; std::shared_ptr motion_factory;