diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt index 079a78cd..90e9a5a7 100644 --- a/src/gui/wxgui/CMakeLists.txt +++ b/src/gui/wxgui/CMakeLists.txt @@ -88,6 +88,8 @@ add_library(CemuWxGui STATIC input/settings/DefaultControllerSettings.h input/settings/WiimoteControllerSettings.cpp input/settings/WiimoteControllerSettings.h + input/settings/MouseControllerSettings.cpp + input/settings/MouseControllerSettings.h LoggingWindow.cpp LoggingWindow.h MainWindow.cpp diff --git a/src/gui/wxgui/MainWindow.cpp b/src/gui/wxgui/MainWindow.cpp index e1e88ff8..7354d5a7 100644 --- a/src/gui/wxgui/MainWindow.cpp +++ b/src/gui/wxgui/MainWindow.cpp @@ -1383,6 +1383,12 @@ void MainWindow::OnMouseMove(wxMouseEvent& event) std::unique_lock lock(instance.m_main_mouse.m_mutex); auto physPos = ToPhys(event.GetPosition()); instance.m_main_mouse.position = { physPos.x, physPos.y }; + auto renderCanvasMouse = m_render_canvas->ScreenToClient(m_mouse_position); + instance.SetMousePositionInRenderBox + ({ + static_cast(renderCanvasMouse.x) / m_render_canvas->GetClientSize().GetWidth(), + static_cast(renderCanvasMouse.y) / m_render_canvas->GetClientSize().GetHeight(), + }); lock.unlock(); if (!IsFullScreen()) diff --git a/src/gui/wxgui/input/InputAPIAddWindow.cpp b/src/gui/wxgui/input/InputAPIAddWindow.cpp index b10a2c75..c3fcde60 100644 --- a/src/gui/wxgui/input/InputAPIAddWindow.cpp +++ b/src/gui/wxgui/input/InputAPIAddWindow.cpp @@ -173,10 +173,23 @@ void InputAPIAddWindow::on_api_selected(wxCommandEvent& event) m_controller_list->SetSelection(wxNOT_FOUND); const auto selection = m_input_api->GetStringSelection(); - // keyboard is a special case, as theres only one device supported atm + // keyboard and mouse are special cases, as theres only one device supported atm + bool isSingleDeviceOnly{false}; + InputAPI::Type singleDeviceAPI; if (selection == wxString::FromUTF8(to_string(InputAPI::Keyboard))) { - const auto controllers = InputManager::instance().get_api_provider(InputAPI::Keyboard)->get_controllers(); + isSingleDeviceOnly = true; + singleDeviceAPI = InputAPI::Keyboard; + } + else if (selection == wxString::FromUTF8(to_string(InputAPI::Mouse))) + { + isSingleDeviceOnly = true; + singleDeviceAPI = InputAPI::Mouse; + } + + if (isSingleDeviceOnly) + { + const auto controllers = InputManager::instance().get_api_provider(singleDeviceAPI)->get_controllers(); if (!controllers.empty()) { m_controller = controllers[0]; diff --git a/src/gui/wxgui/input/InputSettings2.cpp b/src/gui/wxgui/input/InputSettings2.cpp index d77e1430..dae93b8f 100644 --- a/src/gui/wxgui/input/InputSettings2.cpp +++ b/src/gui/wxgui/input/InputSettings2.cpp @@ -25,6 +25,7 @@ #include "wxgui/input/PairingDialog.h" #endif +#include "input/api/Mouse/MouseController.h" #include "wxgui/input/panels/VPADInputPanel.h" #include "wxgui/input/panels/ProControllerInputPanel.h" @@ -35,6 +36,7 @@ #include "util/EventService.h" #include "resource/embedded/resources.h" +#include "settings/MouseControllerSettings.h" bool g_inputConfigWindowHasFocus = false; @@ -960,7 +962,14 @@ void InputSettings2::on_controller_settings(wxCommandEvent& event) wnd.ShowModal(); break; } - + case InputAPI::Mouse: + { + const auto mouse_controller = std::dynamic_pointer_cast(controller); + wxASSERT(mouse_controller); + MouseControllerSettings wnd(this, wxGetMousePosition() + wxSize(5, 5), mouse_controller); + wnd.ShowModal(); + break; + } case InputAPI::Keyboard: break; #ifdef SUPPORTS_WIIMOTE diff --git a/src/gui/wxgui/input/settings/MouseControllerSettings.cpp b/src/gui/wxgui/input/settings/MouseControllerSettings.cpp new file mode 100644 index 00000000..7f877d16 --- /dev/null +++ b/src/gui/wxgui/input/settings/MouseControllerSettings.cpp @@ -0,0 +1,72 @@ +#include "MouseControllerSettings.h" + +#include "input/api/Mouse/MouseController.h" + +#include +#include +#include +#include + +#include "wxgui/helpers/wxHelpers.h" +#include "wxgui/input/InputAPIAddWindow.h" + + +MouseControllerSettings::MouseControllerSettings(wxWindow* parent, const wxPoint& position, std::shared_ptr controller) + : wxDialog(parent, wxID_ANY, _("Controller settings"), position, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE), m_controller(std::move(controller)) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + auto* sizer = new wxBoxSizer(wxVERTICAL); + + // options + { + auto* box = new wxStaticBox(this, wxID_ANY, _("Options")); + auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); + + // Position (wiimote pointer) + m_usePosition = new wxCheckBox(box, wxID_ANY, _("Use position")); + m_usePosition->SetValue(m_controller->IsPositionEnabled()); + m_usePosition->Enable(m_controller->has_position()); + box_sizer->Add(m_usePosition, 0, wxEXPAND | wxALL, 5); + + sizer->Add(box_sizer, 1, wxALL|wxEXPAND, 5); + } + + { + auto* control_sizer = new wxFlexGridSizer(0, 3, 0, 0); + control_sizer->AddGrowableCol(2); + + auto* ok_button = new wxButton(this, wxID_ANY, _("OK")); + ok_button->Bind(wxEVT_BUTTON, [this](auto&) { UpdateSettings(); EndModal(wxID_OK); }); + control_sizer->Add(ok_button, 0, wxALL, 5); + + control_sizer->Add(0, 0, 0, wxEXPAND, 5); + + auto* cancel_button = new wxButton(this, wxID_ANY, _("Cancel")); + cancel_button->Bind(wxEVT_BUTTON, [this](auto&) { EndModal(wxID_CANCEL); }); + control_sizer->Add(cancel_button, 0, wxALL, 5); + + sizer->Add(control_sizer, 0, wxEXPAND, 5); + } + + this->SetSizer(sizer); + this->Layout(); + this->Fit(); + + this->Bind(wxEVT_CLOSE_WINDOW, &MouseControllerSettings::OnClose, this); +} + +void MouseControllerSettings::UpdateSettings() +{ + if (m_usePosition) + m_controller->SetPositionEnabled(m_usePosition->GetValue()); +} + +void MouseControllerSettings::OnClose(wxCloseEvent& event) +{ + if (this->GetReturnCode() == 0 || this->GetReturnCode() == wxID_OK) + UpdateSettings(); + + event.Skip(); +} diff --git a/src/gui/wxgui/input/settings/MouseControllerSettings.h b/src/gui/wxgui/input/settings/MouseControllerSettings.h new file mode 100644 index 00000000..77d58476 --- /dev/null +++ b/src/gui/wxgui/input/settings/MouseControllerSettings.h @@ -0,0 +1,25 @@ +#pragma once + +#include "input/api/Controller.h" +#include "wx/dialog.h" + +#include + +class wxCheckBox; +class wxPoint; +class wxWindow; +class MouseController; + +class MouseControllerSettings : public wxDialog +{ +public: + MouseControllerSettings(wxWindow* parent, const wxPoint& position, std::shared_ptr controller); + +private: + std::shared_ptr m_controller; + + wxCheckBox* m_usePosition = nullptr; + + void UpdateSettings(); + void OnClose(wxCloseEvent& event); +}; diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 4978ef0a..b485f309 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -19,6 +19,10 @@ add_library(CemuInput api/Keyboard/KeyboardControllerProvider.cpp api/Keyboard/KeyboardController.cpp api/Keyboard/KeyboardController.h + api/Mouse/MouseControllerProvider.h + api/Mouse/MouseControllerProvider.cpp + api/Mouse/MouseController.cpp + api/Mouse/MouseController.h emulated/ProController.cpp emulated/EmulatedController.h emulated/EmulatedController.cpp diff --git a/src/input/ControllerFactory.cpp b/src/input/ControllerFactory.cpp index 3827e1d4..4e331815 100644 --- a/src/input/ControllerFactory.cpp +++ b/src/input/ControllerFactory.cpp @@ -1,5 +1,6 @@ #include "input/ControllerFactory.h" +#include "api/Mouse/MouseController.h" #include "input/emulated/VPADController.h" #include "input/emulated/ProController.h" #include "input/emulated/ClassicController.h" @@ -31,6 +32,10 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin case InputAPI::Keyboard: return std::make_shared(); #endif +#if HAS_MOUSE + case InputAPI::Mouse: + return std::make_shared(); +#endif #if HAS_DIRECTINPUT case InputAPI::DirectInput: { diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp index 44973d8d..efbcd84b 100644 --- a/src/input/InputManager.cpp +++ b/src/input/InputManager.cpp @@ -5,6 +5,7 @@ #include #include "Cafe/GameProfile/GameProfile.h" #include "util/EventService.h" +#include "input/api/Mouse/MouseControllerProvider.h" InputManager::InputManager() { @@ -28,6 +29,9 @@ InputManager::InputManager() #if HAS_KEYBOARD create_provider(); #endif +#if HAS_MOUSE + create_provider(); +#endif #ifdef HAS_SDL create_provider(); #endif @@ -233,6 +237,8 @@ bool InputManager::migrate_config(const fs::path& file_path) std::string uuid; if (api_string == to_string(InputAPI::Keyboard)) uuid = to_string(InputAPI::Keyboard); + else if (api_string == to_string(InputAPI::Mouse)) + uuid = to_string(InputAPI::Mouse); else { if (!uuid_opt) @@ -928,6 +934,16 @@ std::optional InputManager::get_right_down_mouse_info(bool* is_pad) return {}; } +glm::vec2 InputManager::GetMousePositionInRenderBox() const +{ + return m_mouseInRenderCanvas; +} + +void InputManager::SetMousePositionInRenderBox(glm::vec2 newMousePosition) +{ + m_mouseInRenderCanvas = newMousePosition; +} + void InputManager::update_thread() { SetThreadName("Input_update"); diff --git a/src/input/InputManager.h b/src/input/InputManager.h index b510474b..9383f5d3 100644 --- a/src/input/InputManager.h +++ b/src/input/InputManager.h @@ -91,10 +91,17 @@ public: glm::ivec2 get_mouse_position(bool pad_window) const; std::optional get_left_down_mouse_info(bool* is_pad); std::optional get_right_down_mouse_info(bool* is_pad); + // Returns the mouse position relative to the render canvas (see MainWindow::m_render_canvas). + // (0, 0) is up left; (1, 1) is down right. The values may be negative or above 1 if the mouse cursor + // is outside the render canvas. At the time of writing this, it is used only for mouse-to-wiimote pointer feature. + glm::vec2 GetMousePositionInRenderBox() const; + void SetMousePositionInRenderBox(glm::vec2 newMousePosition); std::atomic m_mouse_wheel; private: + glm::vec2 m_mouseInRenderCanvas{}; + void update_thread(); std::thread m_update_thread; diff --git a/src/input/api/InputAPI.h b/src/input/api/InputAPI.h index a5f1f926..646042af 100644 --- a/src/input/api/InputAPI.h +++ b/src/input/api/InputAPI.h @@ -7,6 +7,7 @@ namespace InputAPI enum Type { Keyboard, + Mouse, SDLController, XInput, DirectInput, @@ -26,6 +27,8 @@ namespace InputAPI { case Keyboard: return "Keyboard"; + case Mouse: + return "Mouse"; case DirectInput: return "DirectInput"; case XInput: @@ -53,6 +56,8 @@ namespace InputAPI { if (str == to_string(Keyboard)) return Keyboard; + else if (str == to_string(Mouse)) + return Mouse; else if (str == to_string(DirectInput)) return DirectInput; else if (str == to_string(XInput)) diff --git a/src/input/api/Mouse/MouseController.cpp b/src/input/api/Mouse/MouseController.cpp new file mode 100644 index 00000000..fe92231a --- /dev/null +++ b/src/input/api/Mouse/MouseController.cpp @@ -0,0 +1,26 @@ +#include "MouseController.h" +#include "input/api/Controller.h" + +std::string_view MouseController::api_name() const +{ + static_assert(to_string(InputAPI::Mouse) == "Mouse"); + return to_string(InputAPI::Mouse); +} + +MouseController& MouseController::SetPositionEnabled(bool value) +{ + m_isPositionEnabled = value; + return *this; +} + +glm::vec2 MouseController::get_position() +{ + return InputManager::instance().GetMousePositionInRenderBox(); +} + +PositionVisibility MouseController::GetPositionVisibility() +{ + return IsPositionEnabled() + ? PositionVisibility::FULL + : PositionVisibility::NONE; +} diff --git a/src/input/api/Mouse/MouseController.h b/src/input/api/Mouse/MouseController.h new file mode 100644 index 00000000..55953f48 --- /dev/null +++ b/src/input/api/Mouse/MouseController.h @@ -0,0 +1,26 @@ +#pragma once + +#include "MouseControllerProvider.h" +#include "input/api/Controller.h" + + +class MouseController : public Controller +{ +public: + MouseController() : base_type("mouse", "Mouse") {} + + std::string_view api_name() const override; + InputAPI::Type api() const override { return InputAPI::Mouse; } + bool is_connected() override { return true; } + ControllerState raw_state() override { return {}; } + + bool has_position() override { return true; } + bool IsPositionEnabled() const { return m_isPositionEnabled; } + MouseController& SetPositionEnabled(bool value); + glm::vec2 get_position() override; + PositionVisibility GetPositionVisibility() override; + +private: + bool m_isPositionEnabled = false; +}; + diff --git a/src/input/api/Mouse/MouseControllerProvider.cpp b/src/input/api/Mouse/MouseControllerProvider.cpp new file mode 100644 index 00000000..5c96ee85 --- /dev/null +++ b/src/input/api/Mouse/MouseControllerProvider.cpp @@ -0,0 +1,10 @@ +#include "MouseControllerProvider.h" + +#include "MouseController.h" + +std::vector> MouseControllerProvider::get_controllers() +{ + std::vector> result; + result.emplace_back(std::make_shared()); + return result; +} diff --git a/src/input/api/Mouse/MouseControllerProvider.h b/src/input/api/Mouse/MouseControllerProvider.h new file mode 100644 index 00000000..fdf957e1 --- /dev/null +++ b/src/input/api/Mouse/MouseControllerProvider.h @@ -0,0 +1,17 @@ +#pragma once + +#include "input/api/ControllerProvider.h" + +#ifndef HAS_MOUSE +#define HAS_MOUSE 1 +#endif + +class MouseControllerProvider : public ControllerProviderBase +{ +public: + inline static InputAPI::Type kAPIType = InputAPI::Mouse; + InputAPI::Type api() const override { return kAPIType; } + + std::vector> get_controllers() override; +}; +