diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index fee8fa4327..373017197a 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -81,7 +81,7 @@ if (NOT ANDROID) # Unix display manager if(X11_FOUND) - target_link_libraries(rpcs3_lib PRIVATE X11::X11) + target_link_libraries(rpcs3_lib PRIVATE X11::X11 xkbcommon-x11) elseif(USE_VULKAN AND UNIX AND NOT WAYLAND_FOUND AND NOT APPLE AND NOT ANDROID) # Wayland has been checked in 3rdparty/CMakeLists.txt already. message(FATAL_ERROR "RPCS3 requires either X11 or Wayland (or both) for Vulkan.") diff --git a/rpcs3/Emu/Cell/Modules/cellKb.cpp b/rpcs3/Emu/Cell/Modules/cellKb.cpp index 0682d32fc8..adfa849bfb 100644 --- a/rpcs3/Emu/Cell/Modules/cellKb.cpp +++ b/rpcs3/Emu/Cell/Modules/cellKb.cpp @@ -381,7 +381,7 @@ error_code cellKbRead(u32 port_no, vm::ptr data) data->keycode[i] = current_data.buttons[i].m_keyCode; } - KbConfig& current_config = consumer.GetConfig(port_no); + const KbConfig& current_config = consumer.GetConfig(port_no); // For single character mode to work properly we need to "flush" the buffer after reading or else we'll constantly get the same key presses with each call. // Actual key repeats are handled by adding a new key code to the buffer periodically. Key releases are handled in a similar fashion. diff --git a/rpcs3/Emu/Io/KeyboardHandler.h b/rpcs3/Emu/Io/KeyboardHandler.h index 594f7be3f8..2a5261ff8b 100644 --- a/rpcs3/Emu/Io/KeyboardHandler.h +++ b/rpcs3/Emu/Io/KeyboardHandler.h @@ -128,9 +128,9 @@ public: KbInfo& GetInfo() { return m_info; } std::vector& GetKeyboards() { return m_keyboards; } - KbData& GetData(const u32 keyboard) { return m_keyboards[keyboard].m_data; } - KbExtraData& GetExtraData(const u32 keyboard) { return m_keyboards[keyboard].m_extra_data; } - KbConfig& GetConfig(const u32 keyboard) { return m_keyboards[keyboard].m_config; } + KbData& GetData(const u32 keyboard) { return ::at32(m_keyboards, keyboard).m_data; } + KbExtraData& GetExtraData(const u32 keyboard) { return ::at32(m_keyboards, keyboard).m_extra_data; } + KbConfig& GetConfig(const u32 keyboard) { return ::at32(m_keyboards, keyboard).m_config; } identifier id() const { return m_id; } void ReleaseAllKeys(); diff --git a/rpcs3/Input/basic_keyboard_handler.cpp b/rpcs3/Input/basic_keyboard_handler.cpp index 3fa7acf876..a40190f3e8 100644 --- a/rpcs3/Input/basic_keyboard_handler.cpp +++ b/rpcs3/Input/basic_keyboard_handler.cpp @@ -7,6 +7,11 @@ #ifdef _WIN32 #include "windows.h" +#elif __linux__ +#include +#include +#include +#include #endif LOG_CHANNEL(input_log, "Input"); @@ -44,7 +49,7 @@ void basic_keyboard_handler::Init(keyboard_consumer& consumer, const u32 max_con /* Sets the target window for the event handler, and also installs an event filter on the target. */ void basic_keyboard_handler::SetTargetWindow(QWindow* target) { - if (target != nullptr) + if (target) { m_target = target; target->installEventFilter(this); @@ -119,7 +124,7 @@ void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent) return; } - const int key = getUnmodifiedKey(keyEvent); + const int key = get_unmodified_key(keyEvent); if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), true, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String())) { @@ -140,7 +145,7 @@ void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent) return; } - const int key = getUnmodifiedKey(keyEvent); + const int key = get_unmodified_key(keyEvent); if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), false, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String())) { @@ -149,8 +154,10 @@ void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent) } // This should get the actual unmodified key without getting too crazy. -// key() only shows the modifiers and the modified key (e.g. no easy way of knowing that - was pressed in 'SHIFT+-' in order to get _) -s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent) +// key() only shows the modifiers and the modified key. +// e.g. 'Shift+1' may result in Qt::Key_Exclam, so we lose the information that Qt::Key_1 was pressed. +// We want to find the actual physical key that was pressed (and return Qt::Key_1 in this example). +s32 basic_keyboard_handler::get_unmodified_key(QKeyEvent* keyEvent) { if (!keyEvent) [[unlikely]] { @@ -166,9 +173,9 @@ s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent) u32 raw_key = static_cast(key); -#ifdef _WIN32 if (keyEvent->modifiers() != Qt::NoModifier && !keyEvent->text().isEmpty()) { +#ifdef _WIN32 u32 mapped_key = static_cast(MapVirtualKeyA(static_cast(keyEvent->nativeVirtualKey()), MAPVK_VK_TO_CHAR)); if (raw_key != mapped_key) @@ -179,8 +186,72 @@ s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent) } raw_key = mapped_key; } - } +#elif __linux__ + class kb_mapper + { + public: + kb_mapper() + { + m_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + } + + ~kb_mapper() + { + if (m_ctx) xkb_context_unref(m_ctx); + } + + s32 get_unmodified_key(u32 qt_native_scan_code) + { + if (!m_ctx) return -1; + + auto* connection = get_connection(); + if (!connection) return -1; + + const int device_id = xkb_x11_get_core_keyboard_device_id(connection); + + xkb_keymap* keymap = xkb_x11_keymap_new_from_device(m_ctx, connection, device_id, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!keymap) return -1; + + xkb_state* state = xkb_x11_state_new_from_device(keymap, connection, device_id); + if (!state) + { + xkb_keymap_unref(keymap); + return -1; + } + + const xkb_keycode_t code = static_cast(qt_native_scan_code); + const xkb_layout_index_t layout = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_EFFECTIVE); + const xkb_keysym_t* syms = nullptr; + const int count = xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &syms); + + const auto new_key = (syms && count > 0) ? QXkbCommon::keysymToQtKey(syms[0], Qt::NoModifier, nullptr, code) : -1; + + xkb_state_unref(state); + xkb_keymap_unref(keymap); + + return new_key; + } + + private: + xcb_connection_t* get_connection() + { + if (!qGuiApp) return nullptr; + auto* native_interface = qGuiApp->nativeInterface(); + return native_interface ? native_interface->connection() : nullptr; + } + + xkb_context* m_ctx = nullptr; + }; + + static kb_mapper mapper = kb_mapper(); + if (const int res = mapper.get_unmodified_key(keyEvent->nativeScanCode()); res > 0) + { + raw_key = res; + } +#elif __APPLE__ + // TODO #endif + } return static_cast(raw_key); } diff --git a/rpcs3/Input/basic_keyboard_handler.h b/rpcs3/Input/basic_keyboard_handler.h index 284890025f..353d054834 100644 --- a/rpcs3/Input/basic_keyboard_handler.h +++ b/rpcs3/Input/basic_keyboard_handler.h @@ -17,7 +17,7 @@ public: bool eventFilter(QObject* watched, QEvent* event) override; void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); - static s32 getUnmodifiedKey(QKeyEvent* event); + static s32 get_unmodified_key(QKeyEvent* event); private: void LoadSettings(Keyboard& keyboard);