diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,7 +158,7 @@ set(HAVE_WAYLAND_EGL TRUE) endif() -find_package(XKB 0.4.1) +find_package(XKB 0.5.0) set_package_properties(XKB PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" diff --git a/keyboard_input.h b/keyboard_input.h --- a/keyboard_input.h +++ b/keyboard_input.h @@ -33,6 +33,8 @@ struct xkb_context; struct xkb_keymap; struct xkb_state; +struct xkb_compose_table; +struct xkb_compose_state; typedef uint32_t xkb_mod_index_t; typedef uint32_t xkb_keysym_t; @@ -58,6 +60,9 @@ void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state); xkb_keysym_t toKeysym(uint32_t key); + xkb_keysym_t currentKeysym() const { + return m_keysym; + } QString toString(xkb_keysym_t keysym); Qt::Key toQtKey(xkb_keysym_t keysym); Qt::KeyboardModifiers modifiers() const; @@ -81,11 +86,17 @@ xkb_mod_index_t m_altModifier; xkb_mod_index_t m_metaModifier; Qt::KeyboardModifiers m_modifiers; + xkb_keysym_t m_keysym; struct { uint pressCount = 0; Qt::KeyboardModifier modifier = Qt::NoModifier; } m_modOnlyShortcut; quint32 m_currentLayout = 0; + + struct { + xkb_compose_table *table = nullptr; + xkb_compose_state *state = nullptr; + } m_compose; }; class KeyboardInputRedirection : public QObject diff --git a/keyboard_input.cpp b/keyboard_input.cpp --- a/keyboard_input.cpp +++ b/keyboard_input.cpp @@ -42,6 +42,7 @@ #include // xkbcommon #include +#include #include // system #include @@ -90,12 +91,31 @@ , m_altModifier(0) , m_metaModifier(0) , m_modifiers(Qt::NoModifier) + , m_keysym(XKB_KEY_NoSymbol) { if (!m_context) { qCDebug(KWIN_XKB) << "Could not create xkb context"; } else { xkb_context_set_log_level(m_context, XKB_LOG_LEVEL_DEBUG); xkb_context_set_log_fn(m_context, &xkbLogHandler); + + // get locale as described in xkbcommon doc + // cannot use QLocale as it drops the modifier part + QByteArray locale = qgetenv("LC_ALL"); + if (locale.isEmpty()) { + locale = qgetenv("LC_CTYPE"); + } + if (locale.isEmpty()) { + locale = qgetenv("LANG"); + } + if (locale.isEmpty()) { + locale = QByteArrayLiteral("C"); + } + + m_compose.table = xkb_compose_table_new_from_locale(m_context, locale.constData(), XKB_COMPOSE_COMPILE_NO_FLAGS); + if (m_compose.table) { + m_compose.state = xkb_compose_state_new(m_compose.table, XKB_COMPOSE_STATE_NO_FLAGS); + } } auto resetModOnlyShortcut = [this] { @@ -108,6 +128,8 @@ Xkb::~Xkb() { + xkb_compose_state_unref(m_compose.state); + xkb_compose_table_unref(m_compose.table); xkb_state_unref(m_state); xkb_keymap_unref(m_keymap); xkb_context_unref(m_context); @@ -254,6 +276,24 @@ } const auto oldMods = m_modifiers; xkb_state_update_key(m_state, key + 8, static_cast(state)); + if (state == InputRedirection::KeyboardKeyPressed) { + const auto sym = toKeysym(key); + if (m_compose.state && xkb_compose_state_feed(m_compose.state, sym) == XKB_COMPOSE_FEED_ACCEPTED) { + switch (xkb_compose_state_get_status(m_compose.state)) { + case XKB_COMPOSE_NOTHING: + m_keysym = sym; + break; + case XKB_COMPOSE_COMPOSED: + m_keysym = xkb_compose_state_get_one_sym(m_compose.state); + break; + default: + m_keysym = XKB_KEY_NoSymbol; + break; + } + } else { + m_keysym = sym; + } + } updateModifiers(); if (state == InputRedirection::KeyboardKeyPressed) { m_modOnlyShortcut.pressCount++; @@ -525,13 +565,13 @@ } } - const xkb_keysym_t keySym = m_xkb->toKeysym(key); + const xkb_keysym_t keySym = m_xkb->currentKeysym(); KeyEvent event(type, m_xkb->toQtKey(keySym), m_xkb->modifiers(), key, keySym, - m_xkb->toString(m_xkb->toKeysym(key)), + m_xkb->toString(keySym), autoRepeat, time, device);