From 9f02733a0dfa7f8e35bbe0ed245b443b23c6f4a8 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sun, 18 Sep 2022 18:07:26 -0700 Subject: [PATCH] Use unordered_map for keydown to allow codes above 255 (#248) - Adds internal support for 32bit key codes, required for proper keyboard input on Linux - Use gdk_keyval_name to get key name on Linux --- src/gui/CemuApp.cpp | 12 +++--- src/gui/guiWrapper.cpp | 18 +++++--- src/gui/guiWrapper.h | 41 ++++++++++++++++++- src/gui/input/panels/InputPanel.cpp | 39 ++++++++++-------- src/imgui/imgui_extension.cpp | 17 ++++++-- src/input/api/Controller.cpp | 27 ++++++------ src/input/api/ControllerState.h | 3 +- src/input/api/DSU/DSUController.cpp | 6 +-- .../api/DirectInput/DirectInputController.cpp | 20 ++++----- src/input/api/Keyboard/KeyboardController.cpp | 23 ++++------- src/input/api/SDL/SDLController.cpp | 2 +- .../api/Wiimote/NativeWiimoteController.cpp | 11 +++-- src/input/api/XInput/XInputController.cpp | 3 +- src/input/emulated/EmulatedController.cpp | 4 +- 14 files changed, 143 insertions(+), 83 deletions(-) diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index c3270bed..de60f8bc 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -170,16 +170,16 @@ int CemuApp::FilterEvent(wxEvent& event) { const auto& key_event = (wxKeyEvent&)event; wxGetKeyState(wxKeyCode::WXK_F17); - uint32 keycode=fix_raw_keycode(key_event.GetRawKeyCode(), key_event.GetRawKeyFlags()); - if(keycode<256) - g_window_info.keydown[keycode] = true; + g_window_info.set_keystate(fix_raw_keycode(key_event.GetRawKeyCode(), key_event.GetRawKeyFlags()), true); } else if(event.GetEventType() == wxEVT_KEY_UP) { const auto& key_event = (wxKeyEvent&)event; - uint32 keycode=fix_raw_keycode(key_event.GetRawKeyCode(), key_event.GetRawKeyFlags()); - if(keycode<256) - g_window_info.keydown[keycode] = false; + g_window_info.set_keystate(fix_raw_keycode(key_event.GetRawKeyCode(), key_event.GetRawKeyFlags()), false); + } + else if(event.GetEventType() == wxEVT_KILL_FOCUS) + { + g_window_info.set_keystatesdown(); } return wxApp::FilterEvent(event); diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index 6c06f4f9..835dc499 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -153,6 +153,15 @@ bool gui_isPadWindowOpen() #include typedef void GdkDisplay; +namespace +{ +const char* (*gdk_keyval_name)(unsigned int keyval); +} +std::string gui_gtkRawKeyCodeToString(uint32 keyCode) +{ + return gdk_keyval_name(keyCode); +} + #endif void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, class wxWindow* wxw) @@ -176,9 +185,11 @@ void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, c Window (*dyn_gdk_x11_window_get_xid)(GdkWindow *window); dyn_gdk_x11_window_get_xid = (Window(*)(GdkWindow *window))dlsym(RTLD_NEXT, "gdk_x11_window_get_xid"); + gdk_keyval_name = (const char* (*)(unsigned int))dlsym(RTLD_NEXT, "gdk_keyval_name"); + if(!dyn_gtk_widget_realize || !dyn_gtk_widget_get_window || !dyn_gdk_window_get_display || !dyn_gdk_x11_display_get_xdisplay || - !dyn_gdk_x11_window_get_xid) + !dyn_gdk_x11_window_get_xid || !gdk_keyval_name) { cemuLog_log(LogType::Force, "Unable to load GDK symbols"); return; @@ -206,10 +217,7 @@ void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, c bool gui_isKeyDown(int key) { - if (key >= 256) - return false; - - return g_window_info.keydown[key]; + return g_window_info.get_keystate(key); } void gui_notifyGameLoaded() diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index a9d3e1b3..ce041efb 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -41,14 +41,48 @@ struct WindowInfo std::atomic_bool has_screenshot_request; std::atomic_bool is_fullscreen; - std::atomic_bool keydown[256]{}; - + void set_keystate(uint32 keycode, bool state) + { + const std::lock_guard lock(keycode_mutex); + m_keydown[keycode] = state; + }; + bool get_keystate(uint32 keycode) + { + const std::lock_guard lock(keycode_mutex); + auto result = m_keydown.find(keycode); + if (result == m_keydown.end()) + return false; + return result->second; + } + void get_keystates(std::unordered_map& buttons_out) + { + const std::lock_guard lock(keycode_mutex); + for (auto&& button : m_keydown) + { + buttons_out[button.first] = button.second; + } + } + void set_keystatesdown() + { + const std::lock_guard lock(keycode_mutex); + std::for_each(m_keydown.begin(), m_keydown.end(), [](std::pair& el){ el.second = false; }); + } + template + void iter_keystates(fn f) + { + const std::lock_guard lock(keycode_mutex); + std::for_each(m_keydown.cbegin(), m_keydown.cend(), f); + } WindowHandleInfo window_main; WindowHandleInfo window_pad; // canvas WindowHandleInfo canvas_main; WindowHandleInfo canvas_pad; + private: + std::mutex keycode_mutex; + // m_keydown keys must be valid ImGuiKey values + std::unordered_map m_keydown; }; void gui_create(); @@ -68,6 +102,9 @@ bool gui_isFullScreen(); void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, class wxWindow* wxw); +#if BOOST_OS_LINUX +std::string gui_gtkRawKeyCodeToString(uint32 keyCode); +#endif /* * Returns true if a screenshot request is queued * Once this function has returned true, it will reset back to diff --git a/src/gui/input/panels/InputPanel.cpp b/src/gui/input/panels/InputPanel.cpp index 5d07c643..b01157c8 100644 --- a/src/gui/input/panels/InputPanel.cpp +++ b/src/gui/input/panels/InputPanel.cpp @@ -41,7 +41,7 @@ void InputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, cons } static bool s_was_idle = true; - if (state.buttons.none()) { + if (!std::any_of(state.buttons.begin(), state.buttons.end(), [](auto el){ return el.second; })) { s_was_idle = true; return; } @@ -49,58 +49,65 @@ void InputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, cons if (!s_was_idle) { return; } - - s_was_idle = false; - for (size_t i = 0; i < state.buttons.size(); ++i) + auto get_button_state = [&](uint32 key_id) { - if (state.buttons[i]) + auto result = state.buttons.find(key_id); + if (result == state.buttons.end()) + return false; + return result->second; + }; + s_was_idle = false; + for(auto && button : state.buttons) + { + if (button.second) { + auto id=button.first; if (controller->has_axis()) { // test if one axis direction is pressed more than the other - if ((i == kAxisXP || i == kAxisXN) && (state.buttons[kAxisYP] || state.buttons[kAxisYN])) + if ((id == kAxisXP || id == kAxisXN) && (get_button_state(kAxisYP) || get_button_state(kAxisYN))) { if (std::abs(state.axis.y) > std::abs(state.axis.x)) continue; } - else if ((i == kAxisYP || i == kAxisYN) && (state.buttons[kAxisXP] || state.buttons[kAxisXN])) + else if ((id == kAxisYP || id == kAxisYN) && (get_button_state(kAxisXP) || get_button_state(kAxisXN))) { if (std::abs(state.axis.x) > std::abs(state.axis.y)) continue; } - else if ((i == kRotationXP || i == kRotationXN) && (state.buttons[kRotationYP] || state.buttons[kRotationYN])) + else if ((id == kRotationXP || id == kRotationXN) && (get_button_state(kRotationYP) || get_button_state(kRotationYN))) { if (std::abs(state.rotation.y) > std::abs(state.rotation.x)) continue; } - else if ((i == kRotationYP || i == kRotationYN) && (state.buttons[kRotationXP] || state.buttons[kRotationXN])) + else if ((id == kRotationYP || id == kRotationYN) && (get_button_state(kRotationXP) || get_button_state(kRotationXN))) { if (std::abs(state.rotation.x) > std::abs(state.rotation.y)) continue; } - else if ((i == kTriggerXP || i == kTriggerXN) && (state.buttons[kTriggerYP] || state.buttons[kTriggerYN])) + else if ((id == kTriggerXP || id == kTriggerXN) && (get_button_state(kTriggerYP) || get_button_state(kTriggerYN))) { if (std::abs(state.trigger.y) > std::abs(state.trigger.x)) continue; } - else if ((i == kTriggerYP || i == kTriggerYN) && (state.buttons[kTriggerXP] || state.buttons[kTriggerXN])) + else if ((id == kTriggerYP || id == kTriggerYN) && (get_button_state(kTriggerXP) || get_button_state(kTriggerXN))) { if (std::abs(state.trigger.x) > std::abs(state.trigger.y)) continue; } // ignore too low button values on configuration - if (i >= kButtonAxisStart) + if (id >= kButtonAxisStart) { - if (controller->get_axis_value(i) < 0.33f) { - forceLogDebug_printf("skipping since value too low %f", controller->get_axis_value(i)); + if (controller->get_axis_value(id) < 0.33f) { + forceLogDebug_printf("skipping since value too low %f", controller->get_axis_value(id)); s_was_idle = true; return; } } } - emulated_controller->set_mapping(mapping, controller, i); - element->SetValue(controller->get_button_name(i)); + emulated_controller->set_mapping(mapping, controller, id); + element->SetValue(controller->get_button_name(id)); element->SetBackgroundColour(kKeyColourNormalMode); m_color_backup[element->GetId()] = kKeyColourNormalMode; break; diff --git a/src/imgui/imgui_extension.cpp b/src/imgui/imgui_extension.cpp index 567a1e9a..c5b1a6be 100644 --- a/src/imgui/imgui_extension.cpp +++ b/src/imgui/imgui_extension.cpp @@ -110,7 +110,8 @@ ImFont* ImGui_GetFont(float size) void ImGui_UpdateWindowInformation(bool mainWindow) { extern WindowInfo g_window_info; - + static std::map keyboard_mapping; + static uint32 current_key = 0; ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; @@ -130,9 +131,17 @@ void ImGui_UpdateWindowInformation(bool mainWindow) bool padDown; const auto pos = instance.get_left_down_mouse_info(&padDown); io.MouseDown[0] = padDown != mainWindow && pos.has_value(); - - std::fill_n(io.KeysDown, std::size(io.KeysDown), false); - std::copy(std::cbegin(g_window_info.keydown), std::cend(g_window_info.keydown), io.KeysDown); + auto get_mapping = [&](uint32 key_code) + { + auto key = keyboard_mapping.find(key_code); + if (key != keyboard_mapping.end()) + return key->second; + ImGuiKey mapped_key = current_key + ImGuiKey_NamedKey_BEGIN; + current_key = (current_key + 1) % ImGuiKey_NamedKey_COUNT ; + keyboard_mapping[key_code] = mapped_key; + return mapped_key; + }; + g_window_info.iter_keystates([&](auto&& el){ io.AddKeyEvent(get_mapping(el.first), el.second); }); // printf("%f %f %d\n", io.MousePos.x, io.MousePos.y, io.MouseDown[0]); diff --git a/src/input/api/Controller.cpp b/src/input/api/Controller.cpp index 0027392b..a75cdf57 100644 --- a/src/input/api/Controller.cpp +++ b/src/input/api/Controller.cpp @@ -15,8 +15,10 @@ const ControllerState& ControllerBase::update_state() ControllerState result = raw_state(); // ignore default buttons - result.buttons &= ~m_default_state.buttons; - + for (auto&& el : m_default_state.buttons) + { + result.buttons[el.first] = result.buttons[el.first] && !el.second; + } // apply deadzone and range and ignore default axis values apply_axis_setting(result.axis, m_default_state.axis, m_settings.axis); apply_axis_setting(result.rotation, m_default_state.rotation, m_settings.rotation); @@ -24,22 +26,22 @@ const ControllerState& ControllerBase::update_state() #define APPLY_AXIS_BUTTON(_axis_, _flag_) \ if (result._axis_.x < -ControllerState::kAxisThreshold) \ - result.buttons.set((_flag_) + (kAxisXN - kAxisXP)); \ + result.buttons[(_flag_) + (kAxisXN - kAxisXP)]=true; \ else if (result._axis_.x > ControllerState::kAxisThreshold) \ - result.buttons.set((_flag_)); \ + result.buttons[(_flag_)]=true; \ if (result._axis_.y < -ControllerState::kAxisThreshold) \ - result.buttons.set((_flag_) + 1 + (kAxisXN - kAxisXP)); \ + result.buttons[(_flag_) + 1 + (kAxisXN - kAxisXP)]=true; \ else if (result._axis_.y > ControllerState::kAxisThreshold) \ - result.buttons.set((_flag_) + 1); + result.buttons[(_flag_) + 1]=true; if (result.axis.x < -ControllerState::kAxisThreshold) - result.buttons.set((kAxisXP) + (kAxisXN - kAxisXP)); + result.buttons[(kAxisXP) + (kAxisXN - kAxisXP)]=true; else if (result.axis.x > ControllerState::kAxisThreshold) - result.buttons.set((kAxisXP)); + result.buttons[(kAxisXP)]=true; if (result.axis.y < -ControllerState::kAxisThreshold) - result.buttons.set((kAxisXP) + 1 + (kAxisXN - kAxisXP)); + result.buttons[(kAxisXP) + 1 + (kAxisXN - kAxisXP)]=true; else if (result.axis.y > ControllerState::kAxisThreshold) - result.buttons.set((kAxisXP) + 1);; + result.buttons[(kAxisXP) + 1]=true; APPLY_AXIS_BUTTON(rotation, kRotationXP); APPLY_AXIS_BUTTON(trigger, kTriggerXP); @@ -68,7 +70,7 @@ const ControllerState& ControllerBase::update_state() #undef APPLY_AXIS_BUTTON - m_last_state = result; + m_last_state = std::move(result); return m_last_state; } @@ -127,7 +129,8 @@ bool ControllerBase::operator==(const ControllerBase& c) const float ControllerBase::get_axis_value(uint64 button) const { - if (m_last_state.buttons.test(button)) + auto buttonState=m_last_state.buttons.find(button); + if (buttonState!=m_last_state.buttons.end() && buttonState->second) { if (button <= kButtonNoneAxisMAX || !has_axis()) return 1.0f; diff --git a/src/input/api/ControllerState.h b/src/input/api/ControllerState.h index 2739b2ef..a8ff6628 100644 --- a/src/input/api/ControllerState.h +++ b/src/input/api/ControllerState.h @@ -1,6 +1,5 @@ #pragma once -#include #include struct ControllerState @@ -18,7 +17,7 @@ struct ControllerState glm::vec2 rotation{ }; glm::vec2 trigger{ }; - std::bitset<256> buttons{}; + std::unordered_map buttons{}; uint64 last_state = 0; diff --git a/src/input/api/DSU/DSUController.cpp b/src/input/api/DSU/DSUController.cpp index 1a961a4a..c04c8453 100644 --- a/src/input/api/DSU/DSUController.cpp +++ b/src/input/api/DSU/DSUController.cpp @@ -137,7 +137,7 @@ ControllerState DSUController::raw_state() { if (HAS_BIT(state.data.state1, i)) { - result.buttons.set(bitindex); + result.buttons[bitindex]=true; } } @@ -145,12 +145,12 @@ ControllerState DSUController::raw_state() { if (HAS_BIT(state.data.state2, i)) { - result.buttons.set(bitindex); + result.buttons[bitindex]=true; } } if (state.data.touch) - result.buttons.set(kButton16); + result.buttons[kButton16]=true; result.axis.x = (float)state.data.lx / std::numeric_limits::max(); result.axis.x = (result.axis.x * 2.0f) - 1.0f; diff --git a/src/input/api/DirectInput/DirectInputController.cpp b/src/input/api/DirectInput/DirectInputController.cpp index 9db551ed..87bd3685 100644 --- a/src/input/api/DirectInput/DirectInputController.cpp +++ b/src/input/api/DirectInput/DirectInputController.cpp @@ -278,7 +278,7 @@ ControllerState DirectInputController::raw_state() { if (HAS_BIT(state.rgbButtons[i], 7)) { - result.buttons.set(i); + result.buttons[i]=true; } } @@ -316,19 +316,19 @@ ControllerState DirectInputController::raw_state() { switch (pov) { - case 0: result.buttons.set(kButtonUp); + case 0: result.buttons[kButtonUp]=true; break; - case 4500: result.buttons.set(kButtonUp); // up + right - case 9000: result.buttons.set(kButtonRight); + case 4500: result.buttons[kButtonUp]=true; // up + right + case 9000: result.buttons[kButtonRight]=true; break; - case 13500: result.buttons.set(kButtonRight); // right + down - case 18000: result.buttons.set(kButtonDown); + case 13500: result.buttons[kButtonRight] = true; // right + down + case 18000: result.buttons[kButtonDown] = true; break; - case 22500: result.buttons.set(kButtonDown); // down + left - case 27000: result.buttons.set(kButtonLeft); + case 22500: result.buttons[kButtonDown] = true; // down + left + case 27000: result.buttons[kButtonLeft] = true; break; - case 31500: result.buttons.set(kButtonLeft);; // left + up - result.buttons.set(kButtonUp); // left + up + case 31500: result.buttons[kButtonLeft] = true; // left + up + result.buttons[kButtonUp] = true; // left + up break; } } diff --git a/src/input/api/Keyboard/KeyboardController.cpp b/src/input/api/Keyboard/KeyboardController.cpp index d8bc0a04..0d41fe1a 100644 --- a/src/input/api/Keyboard/KeyboardController.cpp +++ b/src/input/api/Keyboard/KeyboardController.cpp @@ -35,9 +35,13 @@ std::string KeyboardController::get_button_name(uint64 button) const char key_name[128]; if (GetKeyNameTextA(scan_code, key_name, std::size(key_name)) != 0) return key_name; -#endif - + else + return fmt::format("key_{}", button); +#elif BOOST_OS_LINUX + return gui_gtkRawKeyCodeToString(button); +#else return fmt::format("key_{}", button); +#endif } extern WindowInfo g_window_info; @@ -47,20 +51,7 @@ ControllerState KeyboardController::raw_state() ControllerState result{}; if (g_window_info.app_active) { - static_assert(result.buttons.size() == std::size(g_window_info.keydown), "invalid size"); - for (uint32 i = wxKeyCode::WXK_BACK; i < result.buttons.size(); ++i) - { - if(const bool v = g_window_info.keydown[i]) - { - result.buttons.set(i, v); - } - } - // ignore generic key codes on Windows when there is also a left/right variant -#if BOOST_OS_WINDOWS - result.buttons.set(VK_SHIFT, false); - result.buttons.set(VK_CONTROL, false); - result.buttons.set(VK_MENU, false); -#endif + g_window_info.get_keystates(result.buttons); } return result; } diff --git a/src/input/api/SDL/SDLController.cpp b/src/input/api/SDL/SDLController.cpp index 0a0e3067..63f7f4d3 100644 --- a/src/input/api/SDL/SDLController.cpp +++ b/src/input/api/SDL/SDLController.cpp @@ -147,7 +147,7 @@ ControllerState SDLController::raw_state() { if (m_buttons[i] && SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)i)) { - result.buttons.set(i); + result.buttons[i]=true; } } diff --git a/src/input/api/Wiimote/NativeWiimoteController.cpp b/src/input/api/Wiimote/NativeWiimoteController.cpp index 1396f1b4..c5059f7c 100644 --- a/src/input/api/Wiimote/NativeWiimoteController.cpp +++ b/src/input/api/Wiimote/NativeWiimoteController.cpp @@ -206,23 +206,26 @@ ControllerState NativeWiimoteController::raw_state() return result; const auto state = m_provider->get_state(m_index); - result.buttons = state.buttons; + for (int i = 0; i < std::numeric_limits::digits; i++) + result.buttons[i] = state.buttons & (1<(state.m_extension)) { const auto nunchuck = std::get(state.m_extension); if (nunchuck.c) - result.buttons.set(kWiimoteButton_C); + result.buttons[kWiimoteButton_C]=true; if (nunchuck.z) - result.buttons.set(kWiimoteButton_Z); + result.buttons[kWiimoteButton_Z]=true; result.axis = nunchuck.axis; } else if (std::holds_alternative(state.m_extension)) { const auto classic = std::get(state.m_extension); - result.buttons |= (uint64)classic.buttons << kHighestWiimote; + uint64 buttons = (uint64)classic.buttons << kHighestWiimote; + for (int i = 0; i < std::numeric_limits::digits; i++) + result.buttons[i] = result.buttons[i] || (buttons & (1 << i)); result.axis = classic.left_axis; result.rotation = classic.right_axis; diff --git a/src/input/api/XInput/XInputController.cpp b/src/input/api/XInput/XInputController.cpp index 08d6510d..e2be29b6 100644 --- a/src/input/api/XInput/XInputController.cpp +++ b/src/input/api/XInput/XInputController.cpp @@ -120,7 +120,8 @@ ControllerState XInputController::raw_state() } // Buttons - result.buttons = state.Gamepad.wButtons; + for(int i=0;i::digits;i++) + result.buttons[i] = state.Gamepad.wButtons & (1< 0) result.axis.x = (float)state.Gamepad.sThumbLX / std::numeric_limits::max(); diff --git a/src/input/emulated/EmulatedController.cpp b/src/input/emulated/EmulatedController.cpp index 69c058f7..0028db58 100644 --- a/src/input/emulated/EmulatedController.cpp +++ b/src/input/emulated/EmulatedController.cpp @@ -280,7 +280,9 @@ bool EmulatedController::is_mapping_down(uint64 mapping) const if (it != m_mappings.cend()) { if (const auto controller = it->second.controller.lock()) { - return controller->get_state().buttons.test(it->second.button); + auto& buttons=controller->get_state().buttons; + auto buttonState=buttons.find(it->second.button); + return buttonState!=buttons.end() && buttonState->second; } }