diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index df153a10..ac96b239 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -345,11 +345,12 @@ void CemuConfig::Load(XMLConfigParser& parser) // hotkeys auto xml_hotkeys = parser.get("Hotkeys"); - hotkeys.exitFullscreen.raw = xml_hotkeys.get("ExitFullscreen", WXK_ESCAPE); - hotkeys.toggleFullscreen.raw = xml_hotkeys.get("ToggleFullscreen", WXK_F11); - hotkeys.toggleFullscreenAlt.raw = xml_hotkeys.get("ToggleFullscreenAlt", uHotkey{WXK_CONTROL_M, true}.raw); // ALT+ENTER - hotkeys.takeScreenshot.raw = xml_hotkeys.get("TakeScreenshot", WXK_F12); - hotkeys.toggleFastForward.raw = xml_hotkeys.get("ToggleFastforward", WXK_NONE); + hotkeys.modifiers = xml_hotkeys.get("modifiers", sHotkeyCfg{}); + hotkeys.exitFullscreen = xml_hotkeys.get("ExitFullscreen", sHotkeyCfg{uKeyboardHotkey{WXK_ESCAPE}}); + hotkeys.toggleFullscreen = xml_hotkeys.get("ToggleFullscreen", sHotkeyCfg{uKeyboardHotkey{WXK_F11}}); + hotkeys.toggleFullscreenAlt = xml_hotkeys.get("ToggleFullscreenAlt", sHotkeyCfg{uKeyboardHotkey{WXK_CONTROL_M, true}}); // ALT+ENTER + hotkeys.takeScreenshot = xml_hotkeys.get("TakeScreenshot", sHotkeyCfg{uKeyboardHotkey{WXK_F12}}); + hotkeys.toggleFastForward = xml_hotkeys.get("ToggleFastForward", sHotkeyCfg{}); // emulatedusbdevices auto usbdevices = parser.get("EmulatedUsbDevices"); @@ -554,11 +555,12 @@ void CemuConfig::Save(XMLConfigParser& parser) // hotkeys auto xml_hotkeys = config.set("Hotkeys"); - xml_hotkeys.set("ExitFullscreen", hotkeys.exitFullscreen.raw); - xml_hotkeys.set("ToggleFullscreen", hotkeys.toggleFullscreen.raw); - xml_hotkeys.set("ToggleFullscreenAlt", hotkeys.toggleFullscreenAlt.raw); - xml_hotkeys.set("TakeScreenshot", hotkeys.takeScreenshot.raw); - xml_hotkeys.set("ToggleFastForward", hotkeys.toggleFastForward.raw); + xml_hotkeys.set("modifiers", hotkeys.modifiers); + xml_hotkeys.set("ExitFullscreen", hotkeys.exitFullscreen); + xml_hotkeys.set("ToggleFullscreen", hotkeys.toggleFullscreen); + xml_hotkeys.set("ToggleFullscreenAlt", hotkeys.toggleFullscreenAlt); + xml_hotkeys.set("TakeScreenshot", hotkeys.takeScreenshot); + xml_hotkeys.set("ToggleFastForward", hotkeys.toggleFastForward); // emulated usb devices auto usbdevices = config.set("EmulatedUsbDevices"); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index e7a7c94f..f7c646f9 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -195,13 +195,42 @@ typedef union { struct { - uint16_t key : 13; // enough bits for all keycodes - uint16_t alt : 1; - uint16_t ctrl : 1; - uint16_t shift : 1; + uint16 key : 13; // enough bits for all keycodes + uint16 alt : 1; + uint16 ctrl : 1; + uint16 shift : 1; }; - uint16_t raw; -} uHotkey; + uint16 raw; +} uKeyboardHotkey; + +typedef sint16 ControllerHotkey_t; + +struct sHotkeyCfg +{ + uKeyboardHotkey keyboard{WXK_NONE}; + ControllerHotkey_t controller{-1}; // -1 = disabled + + /* for defaults */ + sHotkeyCfg(const uKeyboardHotkey& keyboard = {WXK_NONE}, const ControllerHotkey_t& controller = {-1}) : + keyboard(keyboard), controller(controller) {}; + + /* for reading from xml */ + sHotkeyCfg(const char* xml_values) + { + std::istringstream iss(xml_values); + iss >> keyboard.raw >> controller; + } +}; + +template <> +struct fmt::formatter : formatter +{ + template + auto format(const sHotkeyCfg c, FormatContext &ctx) const { + std::string xml_values = fmt::format("{} {}", c.keyboard.raw, c.controller); + return formatter::format(xml_values, ctx); + } +}; template <> struct fmt::formatter : formatter { @@ -514,11 +543,12 @@ struct CemuConfig // hotkeys struct { - uHotkey toggleFullscreen{}; - uHotkey toggleFullscreenAlt{}; - uHotkey exitFullscreen{}; - uHotkey takeScreenshot{}; - uHotkey toggleFastForward{}; + sHotkeyCfg modifiers; + sHotkeyCfg toggleFullscreen; + sHotkeyCfg toggleFullscreenAlt; + sHotkeyCfg exitFullscreen; + sHotkeyCfg takeScreenshot; + sHotkeyCfg toggleFastForward; } hotkeys{}; // debug diff --git a/src/config/XMLConfig.h b/src/config/XMLConfig.h index 2a32dc56..dbec8a6e 100644 --- a/src/config/XMLConfig.h +++ b/src/config/XMLConfig.h @@ -7,6 +7,9 @@ #include #include +template +concept HasConstCharConstructor = requires { T(std::declval()); }; + class XMLConfigParser { public: @@ -43,7 +46,7 @@ public: return element->Int64Text(default_value); else if constexpr (std::is_same_v) // doesnt support real uint64... return (uint64)element->Int64Text((sint64)default_value); - else if constexpr (std::is_same_v || std::is_same_v) + else if constexpr (std::is_same_v || std::is_same_v || HasConstCharConstructor) { const char* text = element->GetText(); return text ? text : default_value; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index f6be72f4..4ec83e2a 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1446,14 +1446,14 @@ void MainWindow::OnKeyUp(wxKeyEvent& event) if (swkbd_hasKeyboardInputHook()) return; - uHotkey hotkey{}; + uKeyboardHotkey hotkey{}; hotkey.key = event.GetKeyCode(); hotkey.alt = event.AltDown(); hotkey.ctrl = event.ControlDown(); hotkey.shift = event.ShiftDown(); - const auto& hotkeyMap = HotkeySettings::s_hotkeyToFuncMap; - const auto it = hotkeyMap.find(hotkey.raw); - if (it != hotkeyMap.end()) + const auto& hotkey_map = HotkeySettings::s_keyboardHotkeyToFuncMap; + const auto it = hotkey_map.find(hotkey.raw); + if (it != hotkey_map.end()) it->second(); } diff --git a/src/gui/input/HotkeySettings.cpp b/src/gui/input/HotkeySettings.cpp index 7738a988..d9398b76 100644 --- a/src/gui/input/HotkeySettings.cpp +++ b/src/gui/input/HotkeySettings.cpp @@ -1,9 +1,11 @@ #include "gui/input/HotkeySettings.h" #include #include +#include "input/InputManager.h" +#include "HotkeySettings.h" extern WindowInfo g_window_info; -const std::unordered_map> HotkeySettings::s_cfgHotkeyToFuncMap{ +const std::unordered_map> HotkeySettings::s_cfgHotkeyToFuncMap{ {&s_cfgHotkeys.toggleFullscreen, [](void) { s_mainWindow->ShowFullScreen(!s_mainWindow->IsFullScreen()); }}, {&s_cfgHotkeys.toggleFullscreenAlt, [](void) { s_mainWindow->ShowFullScreen(!s_mainWindow->IsFullScreen()); }}, {&s_cfgHotkeys.exitFullscreen, [](void) { s_mainWindow->ShowFullScreen(false); }}, @@ -13,35 +15,61 @@ const std::unordered_map> HotkeySettings::s_ struct HotkeyEntry { + enum class InputButtonType : wxWindowID { + Keyboard, + Controller, + }; + std::unique_ptr name; std::unique_ptr keyInput; - uHotkey& hotkey; + std::unique_ptr controllerInput; + sHotkeyCfg& hotkey; - HotkeyEntry(wxStaticText* name, wxButton* keyInput, uHotkey& hotkey) - : name(name), keyInput(keyInput), hotkey(hotkey) + HotkeyEntry(wxStaticText* name, wxButton* keyInput, wxButton* controllerInput, sHotkeyCfg& hotkey) + : name(name), keyInput(keyInput), controllerInput(controllerInput), hotkey(hotkey) { keyInput->SetClientData(&hotkey); + keyInput->SetId(static_cast(InputButtonType::Keyboard)); + controllerInput->SetClientData(&hotkey); + controllerInput->SetId(static_cast(InputButtonType::Controller)); } }; HotkeySettings::HotkeySettings(wxWindow* parent) : wxFrame(parent, wxID_ANY, "Hotkey Settings") { - m_panel = new wxPanel(this); - m_sizer = new wxFlexGridSizer(0, 2, wxSize(0, 0)); + SetIcon(wxICON(X_HOTKEY_SETTINGS)); + m_sizer = new wxFlexGridSizer(0, 3, 10, 10); + m_sizer->AddGrowableCol(1); + m_sizer->AddGrowableCol(2); + + m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE); m_panel->SetSizer(m_sizer); Center(); - CreateHotkey("Toggle fullscreen", s_cfgHotkeys.toggleFullscreen); - CreateHotkey("Take screenshot", s_cfgHotkeys.takeScreenshot); - CreateHotkey("Toggle fast-forward", s_cfgHotkeys.toggleFastForward); + SetActiveController(); + + CreateColumnHeaders(); + + /* global modifier */ + CreateHotkeyRow("Hotkey modifier", s_cfgHotkeys.modifiers); + m_hotkeys.at(0).keyInput->Hide(); + + /* hotkeys */ + CreateHotkeyRow("Toggle fullscreen", s_cfgHotkeys.toggleFullscreen); + CreateHotkeyRow("Take screenshot", s_cfgHotkeys.takeScreenshot); + CreateHotkeyRow("Toggle fast-forward", s_cfgHotkeys.toggleFastForward); + + m_controllerTimer = new wxTimer(this); + Bind(wxEVT_TIMER, &HotkeySettings::OnControllerTimer, this); m_sizer->SetSizeHints(this); } HotkeySettings::~HotkeySettings() { + m_controllerTimer->Stop(); if (m_needToSave) { g_config.Save(); @@ -50,100 +78,232 @@ HotkeySettings::~HotkeySettings() void HotkeySettings::Init(wxFrame* mainWindowFrame) { - s_hotkeyToFuncMap.reserve(s_cfgHotkeyToFuncMap.size()); + s_keyboardHotkeyToFuncMap.reserve(s_cfgHotkeyToFuncMap.size()); for (const auto& [cfgHotkey, func] : s_cfgHotkeyToFuncMap) { - auto hotkeyRaw = cfgHotkey->raw; - if (hotkeyRaw > 0) + auto keyboardHotkey = cfgHotkey->keyboard.raw; + if (keyboardHotkey > 0) { - s_hotkeyToFuncMap[hotkeyRaw] = func; + s_keyboardHotkeyToFuncMap[keyboardHotkey] = func; + } + auto controllerHotkey = cfgHotkey->controller; + if (controllerHotkey > 0) + { + s_controllerHotkeyToFuncMap[controllerHotkey] = func; } } s_mainWindow = mainWindowFrame; } -void HotkeySettings::CreateHotkey(const wxString& label, uHotkey& hotkey) +void HotkeySettings::CreateColumnHeaders(void) { - /* add new hotkey */ + auto* emptySpace = new wxStaticText(m_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + auto* keyboard = new wxStaticText(m_panel, wxID_ANY, "Keyboard", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + auto* controller = new wxStaticText(m_panel, wxID_ANY, "Controller", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + + keyboard->SetMinSize(m_minButtonSize); + controller->SetMinSize(m_minButtonSize); + + auto flags = wxSizerFlags().Expand(); + m_sizer->Add(emptySpace, flags); + m_sizer->Add(keyboard, flags); + m_sizer->Add(controller, flags); +} + +void HotkeySettings::CreateHotkeyRow(const wxString& label, sHotkeyCfg& cfgHotkey) +{ + auto* name = new wxStaticText(m_panel, wxID_ANY, label); + auto* keyInput = new wxButton(m_panel, wxID_ANY, To_wxString(cfgHotkey.keyboard), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBU_EXACTFIT); + auto* controllerInput = new wxButton(m_panel, wxID_ANY, To_wxString(cfgHotkey.controller), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBU_EXACTFIT); + + /* for starting input */ + keyInput->Bind(wxEVT_BUTTON, &HotkeySettings::OnKeyboardHotkeyInputLeftClick, this); + controllerInput->Bind(wxEVT_BUTTON, &HotkeySettings::OnControllerHotkeyInputLeftClick, this); + + /* for cancelling and clearing input */ + keyInput->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(HotkeySettings::OnHotkeyInputRightClick), NULL, this); + controllerInput->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(HotkeySettings::OnHotkeyInputRightClick), NULL, this); + + keyInput->SetMinSize(m_minButtonSize); + controllerInput->SetMinSize(m_minButtonSize); + + auto flags = wxSizerFlags().Expand(); + m_sizer->Add(name, flags); + m_sizer->Add(keyInput, flags); + m_sizer->Add(controllerInput, flags); + + m_hotkeys.emplace_back(name, keyInput, controllerInput, cfgHotkey); +} + +void HotkeySettings::OnControllerTimer(wxTimerEvent& event) +{ + if (m_activeController.expired()) { - auto* name = new wxStaticText(m_panel, wxID_ANY, label, wxDefaultPosition, wxSize(240, 30), wxALIGN_CENTER); - auto* keyInput = new wxButton(m_panel, wxID_ANY, To_wxString(hotkey), wxDefaultPosition, wxSize(120, 30), wxWANTS_CHARS); - - keyInput->Bind(wxEVT_BUTTON, &HotkeySettings::OnHotkeyInputLeftClick, this); - keyInput->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(HotkeySettings::OnHotkeyInputRightClick), NULL, this); - - auto flags = wxSizerFlags().Expand(); - m_sizer->Add(name, flags); - m_sizer->Add(keyInput, flags); - - m_hotkeys.emplace_back(name, keyInput, hotkey); + m_controllerTimer->Stop(); + return; + } + auto& controller = *m_activeController.lock(); + auto buttons = controller.update_state().buttons; + if (!buttons.IsIdle()) + { + for (const auto& newHotkey : buttons.GetButtonList()) + { + m_controllerTimer->Stop(); + auto* inputButton = static_cast(m_controllerTimer->GetClientData()); + auto& cfgHotkey = *static_cast(inputButton->GetClientData()); + auto oldHotkey = cfgHotkey.controller; + cfgHotkey.controller = newHotkey; + /* don't bind modifier to map */ + if (&cfgHotkey != &s_cfgHotkeys.modifiers) + { + s_controllerHotkeyToFuncMap.erase(oldHotkey); + s_controllerHotkeyToFuncMap[newHotkey] = s_cfgHotkeyToFuncMap.at(&cfgHotkey); + } + m_needToSave |= true; + FinalizeInput(inputButton); + return; + } } } -void HotkeySettings::OnHotkeyInputLeftClick(wxCommandEvent& event) +void HotkeySettings::OnKeyboardHotkeyInputLeftClick(wxCommandEvent& event) { auto* inputButton = static_cast(event.GetEventObject()); if (m_activeInputButton) { /* ignore multiple clicks of the same button */ if (inputButton == m_activeInputButton) return; - FinalizeInput(m_activeInputButton); + RestoreInputButton(m_activeInputButton); } inputButton->Bind(wxEVT_KEY_UP, &HotkeySettings::OnKeyUp, this); inputButton->SetLabelText('_'); m_activeInputButton = inputButton; } -void HotkeySettings::OnHotkeyInputRightClick(wxMouseEvent& event) { +void HotkeySettings::OnControllerHotkeyInputLeftClick(wxCommandEvent& event) +{ + auto* inputButton = static_cast(event.GetEventObject()); if (m_activeInputButton) { - FinalizeInput(m_activeInputButton); + /* ignore multiple clicks of the same button */ + if (inputButton == m_activeInputButton) return; + RestoreInputButton(m_activeInputButton); + } + m_controllerTimer->Stop(); + if (!SetActiveController()) + { + return; + } + inputButton->SetLabelText('_'); + m_controllerTimer->SetClientData(inputButton); + m_controllerTimer->Start(25); + m_activeInputButton = inputButton; +} + +void HotkeySettings::OnHotkeyInputRightClick(wxMouseEvent& event) +{ + if (m_activeInputButton) + { + RestoreInputButton(m_activeInputButton); return; } auto* inputButton = static_cast(event.GetEventObject()); - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); - uHotkey newHotkey{}; - if (cfgHotkey.raw != newHotkey.raw) + auto& cfgHotkey = *static_cast(inputButton->GetClientData()); + using InputButtonType = HotkeyEntry::InputButtonType; + switch (static_cast(inputButton->GetId())) { - m_needToSave |= true; - s_hotkeyToFuncMap.erase(cfgHotkey.raw); - cfgHotkey = newHotkey; + case InputButtonType::Keyboard: { + uKeyboardHotkey newHotkey{}; + if (cfgHotkey.keyboard.raw != newHotkey.raw) + { + m_needToSave |= true; + s_keyboardHotkeyToFuncMap.erase(cfgHotkey.keyboard.raw); + cfgHotkey.keyboard = newHotkey; + FinalizeInput(inputButton); + } + } break; + case InputButtonType::Controller: { + ControllerHotkey_t newHotkey{ -1 }; + if (cfgHotkey.controller != newHotkey) + { + m_needToSave |= true; + s_controllerHotkeyToFuncMap.erase(cfgHotkey.controller); + cfgHotkey.controller = newHotkey; + FinalizeInput(inputButton); + } + } break; } - FinalizeInput(inputButton); +} + +bool HotkeySettings::SetActiveController(void) +{ + auto emulated_controller = InputManager::instance().get_controller(0); + if (emulated_controller.use_count() <= 1) + { + return false; + } + const auto& controllers = emulated_controller->get_controllers(); + if (controllers.empty()) + { + return false; + } + m_activeController = controllers.at(0); + return true; } void HotkeySettings::OnKeyUp(wxKeyEvent& event) { auto* inputButton = static_cast(event.GetEventObject()); - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); + auto& cfgHotkey = *static_cast(inputButton->GetClientData()); if (auto keycode = event.GetKeyCode(); IsValidKeycodeUp(keycode)) { - auto oldHotkey = cfgHotkey; - uHotkey newHotkey{}; + auto oldHotkey = cfgHotkey.keyboard; + uKeyboardHotkey newHotkey{}; newHotkey.key = keycode; newHotkey.alt = event.AltDown(); newHotkey.ctrl = event.ControlDown(); newHotkey.shift = event.ShiftDown(); if ((newHotkey.raw != oldHotkey.raw) && - (s_hotkeyToFuncMap.find(newHotkey.raw) == s_hotkeyToFuncMap.end())) + (s_keyboardHotkeyToFuncMap.find(newHotkey.raw) == s_keyboardHotkeyToFuncMap.end())) { m_needToSave |= true; - cfgHotkey = newHotkey; - s_hotkeyToFuncMap.erase(oldHotkey.raw); - s_hotkeyToFuncMap[cfgHotkey.raw] = s_cfgHotkeyToFuncMap.at(&cfgHotkey); + cfgHotkey.keyboard = newHotkey; + s_keyboardHotkeyToFuncMap.erase(oldHotkey.raw); + s_keyboardHotkeyToFuncMap[newHotkey.raw] = s_cfgHotkeyToFuncMap.at(&cfgHotkey); } } - FinalizeInput(inputButton); + FinalizeInput(inputButton); } +template void HotkeySettings::FinalizeInput(wxButton* inputButton) { - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); - inputButton->Unbind(wxEVT_KEY_UP, &HotkeySettings::OnKeyUp, this); - inputButton->SetLabelText(To_wxString(cfgHotkey)); + auto& cfgHotkey = *static_cast(inputButton->GetClientData()); + if constexpr (std::is_same_v) + { + inputButton->Unbind(wxEVT_KEY_UP, &HotkeySettings::OnKeyUp, this); + inputButton->SetLabelText(To_wxString(cfgHotkey.keyboard)); + } else if constexpr (std::is_same_v) + { + inputButton->SetLabelText(To_wxString(cfgHotkey.controller)); + } m_activeInputButton = nullptr; } +void HotkeySettings::RestoreInputButton(wxButton* inputButton) +{ + using InputButtonType = HotkeyEntry::InputButtonType; + switch (static_cast(inputButton->GetId())) + { + case InputButtonType::Keyboard: { + FinalizeInput(inputButton); + } break; + case InputButtonType::Controller: { + FinalizeInput(inputButton); + } + } +} + bool HotkeySettings::IsValidKeycodeUp(int keycode) { switch (keycode) @@ -159,25 +319,34 @@ bool HotkeySettings::IsValidKeycodeUp(int keycode) } } -wxString HotkeySettings::To_wxString(uHotkey hotkey) +wxString HotkeySettings::To_wxString(uKeyboardHotkey hotkey) { - if (hotkey.raw <= 0) + wxString ret_val{}; + if (hotkey.raw) { - return ""; + if (hotkey.alt) + { + ret_val.append("ALT + "); + } + if (hotkey.ctrl) + { + ret_val.append("CTRL + "); + } + if (hotkey.shift) + { + ret_val.append("SHIFT + "); + } + ret_val.append(wxAcceleratorEntry(0, hotkey.key).ToString()); + } + return ret_val; +} + +wxString HotkeySettings::To_wxString(ControllerHotkey_t hotkey) +{ + wxString ret_val{}; + if ((hotkey != -1) && !m_activeController.expired()) + { + ret_val = m_activeController.lock()->get_button_name(hotkey); } - wxString ret_val; - if (hotkey.alt) - { - ret_val.append("ALT + "); - } - if (hotkey.ctrl) - { - ret_val.append("CTRL + "); - } - if (hotkey.shift) - { - ret_val.append("SHIFT + "); - } - ret_val.append(wxAcceleratorEntry(0, hotkey.key).ToString()); return ret_val; } diff --git a/src/gui/input/HotkeySettings.h b/src/gui/input/HotkeySettings.h index 8956fcd3..c4fdfea0 100644 --- a/src/gui/input/HotkeySettings.h +++ b/src/gui/input/HotkeySettings.h @@ -2,6 +2,7 @@ #include #include "config/CemuConfig.h" +#include "input/api/Controller.h" class HotkeyEntry; @@ -9,30 +10,43 @@ class HotkeySettings : public wxFrame { public: static void Init(wxFrame* mainWindowFrame); - inline static std::unordered_map> s_hotkeyToFuncMap{}; + inline static std::unordered_map> s_keyboardHotkeyToFuncMap{}; + inline static std::unordered_map> s_controllerHotkeyToFuncMap{}; + inline static auto& s_cfgHotkeys = GetConfig().hotkeys; HotkeySettings(wxWindow* parent); ~HotkeySettings(); private: inline static wxFrame* s_mainWindow = nullptr; - inline static auto& s_cfgHotkeys = GetConfig().hotkeys; - static const std::unordered_map> s_cfgHotkeyToFuncMap; + static const std::unordered_map> s_cfgHotkeyToFuncMap; wxPanel* m_panel; wxFlexGridSizer* m_sizer; + wxButton* m_activeInputButton{ nullptr }; + wxTimer* m_controllerTimer{ nullptr }; + const wxSize m_minButtonSize{ 200, 30 }; + std::vector m_hotkeys; - wxButton* m_activeInputButton = nullptr; + std::weak_ptr m_activeController{}; bool m_needToSave = false; /* helpers */ - void CreateHotkey(const wxString& label, uHotkey& hotkey); - wxString To_wxString(uHotkey hotkey); + void CreateColumnHeaders(void); + void CreateHotkeyRow(const wxString& label, sHotkeyCfg& cfgHotkey); + wxString To_wxString(uKeyboardHotkey hotkey); + wxString To_wxString(ControllerHotkey_t hotkey); bool IsValidKeycodeUp(int keycode); + bool SetActiveController(void); + + template void FinalizeInput(wxButton* inputButton); + void RestoreInputButton(wxButton* inputButton); /* events */ - void OnHotkeyInputLeftClick(wxCommandEvent& event); + void OnKeyboardHotkeyInputLeftClick(wxCommandEvent& event); + void OnControllerHotkeyInputLeftClick(wxCommandEvent& event); void OnHotkeyInputRightClick(wxMouseEvent& event); void OnKeyUp(wxKeyEvent& event); + void OnControllerTimer(wxTimerEvent& event); }; diff --git a/src/input/api/Controller.cpp b/src/input/api/Controller.cpp index b7831def..e219ad50 100644 --- a/src/input/api/Controller.cpp +++ b/src/input/api/Controller.cpp @@ -1,4 +1,6 @@ #include "input/api/Controller.h" +#include "config/CemuConfig.h" +#include "gui/input/HotkeySettings.h" #include "gui/guiWrapper.h" @@ -67,6 +69,22 @@ const ControllerState& ControllerBase::update_state() #undef APPLY_AXIS_BUTTON + /* hotkey capturing */ + const auto& hotkey_mod = HotkeySettings::s_cfgHotkeys.modifiers.controller; + if ((hotkey_mod >= 0) && result.buttons.GetButtonState(hotkey_mod)) { + const auto& hotkey_map = HotkeySettings::s_controllerHotkeyToFuncMap; + for (const auto& button_id : result.buttons.GetButtonList()) { + const auto it = hotkey_map.find(button_id); + if (it == hotkey_map.end()) + continue; + /* only capture clicks */ + if (m_last_state.buttons.GetButtonState(button_id)) + break; + it->second(); + break; + } + } + m_last_state = std::move(result); return m_last_state; } diff --git a/src/resource/cemu.rc b/src/resource/cemu.rc index 6f78bfc3..fb397056 100644 --- a/src/resource/cemu.rc +++ b/src/resource/cemu.rc @@ -37,6 +37,7 @@ X_SETTINGS ICON "resource\\icons8_automatic_26_x X_GAME_PROFILE ICON "resource\\icons8-compose-filled-50.ico" +X_HOTKEY_SETTINGS ICON "resource\\icons8_hotkeys.ico" #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/src/resource/embedded/X_HOTKEY_SETTINGS.xpm b/src/resource/embedded/X_HOTKEY_SETTINGS.xpm new file mode 100644 index 00000000..719eb8ec --- /dev/null +++ b/src/resource/embedded/X_HOTKEY_SETTINGS.xpm @@ -0,0 +1,70 @@ +/* XPM */ +const char *X_HOTKEY_SETTINGS[] = { +"64 64 2 1", +" c None", +". c #000000", +" ... ", +" .... ", +" .... ", +" .. .... ", +" .. ...... ", +" ... ...... ", +" ... ...... ", +" ... ....... ", +" .... ........ ... ", +" .... ......... ... ", +" .... ......... ... ", +" .... .......... .... ", +" .... ......... .... ", +" ..... ........... ..... ", +" ..... .......... ..... ", +" ........ ........... ...... ", +" ........ ... ....... ...... ", +" ........... ... ....... ...... ", +" ........... ..... ...... ....... ", +" ........... ..... ...... ........ ", +" ............. ...... ...... ......... ", +" ........ .... ...... .... ..... .... ", +" ........ .... ....... .... ...... .... ", +" ...... .... ...... ..... ....... ..... ", +" ...... ........... ............ .... ", +" ...... .......... .......... .... ", +" ..... .......... .......... .... ", +" ..... .......... ......... ..... ", +" ..... .......... ........ ..... ", +" ..... ........ ........ ...... ", +" ..... ..... ..... ..... ", +" ..... ..... .... ...... ", +" ..... ..... ", +" ... .... ", +" . ............................ .. ", +" ................................ ", +" ................................ ", +" .................................... ", +" .................................... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... .......... ", +" .......... .......... ", +" .......... .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .......... ........ .......... ", +" .................................... ", +" .................................... ", +" .................................... ", +" .................................... ", +" ................................ ", +" ................................ ", +" ............................. " +}; diff --git a/src/resource/embedded/resources.cpp b/src/resource/embedded/resources.cpp index 5dbb9d14..2b508c10 100644 --- a/src/resource/embedded/resources.cpp +++ b/src/resource/embedded/resources.cpp @@ -2,6 +2,7 @@ #include "M_WND_ICON128.xpm" #include "X_BOX.xpm" #include "X_SETTINGS.xpm" +#include "X_HOTKEY_SETTINGS.xpm" #include "icons8-checkmark-yes-32.hpng" #include "icons8-error-32.hpng" diff --git a/src/resource/embedded/resources.h b/src/resource/embedded/resources.h index 8cb9fe5d..26e9b759 100644 --- a/src/resource/embedded/resources.h +++ b/src/resource/embedded/resources.h @@ -2,6 +2,7 @@ extern const char* X_GAME_PROFILE_xpm[]; extern const char* M_WND_ICON128_xpm[]; extern const char* X_BOX_xpm[]; extern const char* X_SETTINGS_xpm[]; +extern const char* X_HOTKEY_SETTINGS_xpm[]; extern unsigned char PNG_CHECK_YES_png[573]; extern unsigned char PNG_ERROR_png[472]; diff --git a/src/resource/icons8_hotkeys.ico b/src/resource/icons8_hotkeys.ico new file mode 100644 index 00000000..68f34ac4 Binary files /dev/null and b/src/resource/icons8_hotkeys.ico differ diff --git a/src/resource/icons8_hotkeys.png b/src/resource/icons8_hotkeys.png new file mode 100644 index 00000000..24dd3b35 Binary files /dev/null and b/src/resource/icons8_hotkeys.png differ