Blame SOURCES/0006-client-Allow-QWaylandInputContext-to-accept-composed.patch

869fe3
From d6dd815014564f235fb972eb72d28ccca6cf3549 Mon Sep 17 00:00:00 2001
0cfe39
From: Aleix Pol <aleixpol@kde.org>
0cfe39
Date: Wed, 10 Mar 2021 01:09:13 +0100
869fe3
Subject: [PATCH 06/40] client: Allow QWaylandInputContext to accept composed
0cfe39
 key combinations
0cfe39
0cfe39
At the moment, we are forcing user to choose to either compose or use
0cfe39
the text-input channel. This patch brings some of the QComposeInputContext
0cfe39
functionality in order to let applications understand dead key
0cfe39
combinations like they are supposed to.
0cfe39
0cfe39
Having it in QWaylandInputContext rather than in QWaylandInputDevice
0cfe39
should solve the problems 3aedd01271dc4f4a13103d632df224971ab2b6df had
0cfe39
with 57c4af2b18c0fb1d266b245a107fa6cb876b9d9e, because we are doing it
0cfe39
in the input context rather than before. This way, if the user is
0cfe39
overriding the input method (e.g. by setting QT_IM_MODULE), all the key
0cfe39
strokes will still be properly forwarded to the module to use.
0cfe39
0cfe39
This in turn allows us to solve https://bugs.kde.org/show_bug.cgi?id=411729
0cfe39
and https://bugs.kde.org/show_bug.cgi?id=405388 since we don't need to
0cfe39
choose anymore between physical and virual keyboards anymore.
0cfe39
0cfe39
Pick-to: 5.15
0cfe39
Change-Id: I8601f5d7ae21edf4b3a1191fa75877286e505588
0cfe39
Reviewed-by: David Edmundson <davidedmundson@kde.org>
0cfe39
---
0cfe39
 src/client/qwaylanddisplay_p.h      |  3 -
0cfe39
 src/client/qwaylandinputcontext.cpp | 95 ++++++++++++++++++++++++++++-
0cfe39
 src/client/qwaylandinputcontext_p.h | 21 +++++++
0cfe39
 src/client/qwaylandinputdevice.cpp  |  2 +-
0cfe39
 src/client/qwaylandintegration.cpp  |  8 +--
0cfe39
 5 files changed, 119 insertions(+), 10 deletions(-)
0cfe39
0cfe39
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
0cfe39
index 188e9131..3b092bc8 100644
0cfe39
--- a/src/client/qwaylanddisplay_p.h
0cfe39
+++ b/src/client/qwaylanddisplay_p.h
0cfe39
@@ -175,8 +175,6 @@ public:
0cfe39
     QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
0cfe39
     QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
0cfe39
 
0cfe39
-    bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; }
0cfe39
-
0cfe39
     struct RegistryGlobal {
0cfe39
         uint32_t id;
0cfe39
         QString interface;
0cfe39
@@ -282,7 +280,6 @@ private:
0cfe39
     QReadWriteLock m_frameQueueLock;
0cfe39
 
0cfe39
     bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull();
0cfe39
-    bool mUsingInputContextFromCompositor = false;
0cfe39
 
0cfe39
     void registry_global(uint32_t id, const QString &interface, uint32_t version) override;
0cfe39
     void registry_global_remove(uint32_t id) override;
0cfe39
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
0cfe39
index e9afe05e..ef5aa375 100644
0cfe39
--- a/src/client/qwaylandinputcontext.cpp
0cfe39
+++ b/src/client/qwaylandinputcontext.cpp
0cfe39
@@ -406,6 +406,8 @@ bool QWaylandInputContext::isValid() const
0cfe39
 void QWaylandInputContext::reset()
0cfe39
 {
0cfe39
     qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
0cfe39
+    if (m_composeState)
0cfe39
+        xkb_compose_state_reset(m_composeState);
0cfe39
 
0cfe39
     QPlatformInputContext::reset();
0cfe39
 
0cfe39
@@ -526,9 +528,14 @@ Qt::LayoutDirection QWaylandInputContext::inputDirection() const
0cfe39
     return textInput()->inputDirection();
0cfe39
 }
0cfe39
 
0cfe39
-void QWaylandInputContext::setFocusObject(QObject *)
0cfe39
+void QWaylandInputContext::setFocusObject(QObject *object)
0cfe39
 {
0cfe39
     qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
0cfe39
+#if QT_CONFIG(xkbcommon)
0cfe39
+    m_focusObject = object;
0cfe39
+#else
0cfe39
+    Q_UNUSED(object);
0cfe39
+#endif
0cfe39
 
0cfe39
     if (!textInput())
0cfe39
         return;
0cfe39
@@ -561,6 +568,92 @@ QWaylandTextInput *QWaylandInputContext::textInput() const
0cfe39
     return mDisplay->defaultInputDevice()->textInput();
0cfe39
 }
0cfe39
 
0cfe39
+#if QT_CONFIG(xkbcommon)
0cfe39
+
0cfe39
+void QWaylandInputContext::ensureInitialized()
0cfe39
+{
0cfe39
+    if (m_initialized)
0cfe39
+        return;
0cfe39
+
0cfe39
+    if (!m_XkbContext) {
0cfe39
+        qCWarning(qLcQpaInputMethods) << "error: xkb context has not been set on" << metaObject()->className();
0cfe39
+        return;
0cfe39
+    }
0cfe39
+
0cfe39
+    m_initialized = true;
0cfe39
+    const char *locale = setlocale(LC_CTYPE, "");
0cfe39
+    if (!locale)
0cfe39
+        locale = setlocale(LC_CTYPE, nullptr);
0cfe39
+    qCDebug(qLcQpaInputMethods) << "detected locale (LC_CTYPE):" << locale;
0cfe39
+
0cfe39
+    m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
0cfe39
+    if (m_composeTable)
0cfe39
+        m_composeState = xkb_compose_state_new(m_composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
0cfe39
+
0cfe39
+    if (!m_composeTable) {
0cfe39
+        qCWarning(qLcQpaInputMethods, "failed to create compose table");
0cfe39
+        return;
0cfe39
+    }
0cfe39
+    if (!m_composeState) {
0cfe39
+        qCWarning(qLcQpaInputMethods, "failed to create compose state");
0cfe39
+        return;
0cfe39
+    }
0cfe39
+}
0cfe39
+
0cfe39
+bool QWaylandInputContext::filterEvent(const QEvent *event)
0cfe39
+{
0cfe39
+    auto keyEvent = static_cast<const QKeyEvent *>(event);
0cfe39
+    if (keyEvent->type() != QEvent::KeyPress)
0cfe39
+        return false;
0cfe39
+
0cfe39
+    if (!inputMethodAccepted())
0cfe39
+        return false;
0cfe39
+
0cfe39
+    // lazy initialization - we don't want to do this on an app startup
0cfe39
+    ensureInitialized();
0cfe39
+
0cfe39
+    if (!m_composeTable || !m_composeState)
0cfe39
+        return false;
0cfe39
+
0cfe39
+    xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey());
0cfe39
+
0cfe39
+    switch (xkb_compose_state_get_status(m_composeState)) {
0cfe39
+    case XKB_COMPOSE_COMPOSING:
0cfe39
+        return true;
0cfe39
+    case XKB_COMPOSE_CANCELLED:
0cfe39
+        reset();
0cfe39
+        return false;
0cfe39
+    case XKB_COMPOSE_COMPOSED:
0cfe39
+    {
0cfe39
+        const int size = xkb_compose_state_get_utf8(m_composeState, nullptr, 0);
0cfe39
+        QVarLengthArray<char, 32> buffer(size + 1);
0cfe39
+        xkb_compose_state_get_utf8(m_composeState, buffer.data(), buffer.size());
0cfe39
+        QString composedText = QString::fromUtf8(buffer.constData());
0cfe39
+
0cfe39
+        QInputMethodEvent event;
0cfe39
+        event.setCommitString(composedText);
0cfe39
+
0cfe39
+        if (!m_focusObject && qApp)
0cfe39
+            m_focusObject = qApp->focusObject();
0cfe39
+
0cfe39
+        if (m_focusObject)
0cfe39
+            QCoreApplication::sendEvent(m_focusObject, &event);
0cfe39
+        else
0cfe39
+            qCWarning(qLcQpaInputMethods, "no focus object");
0cfe39
+
0cfe39
+        reset();
0cfe39
+        return true;
0cfe39
+    }
0cfe39
+    case XKB_COMPOSE_NOTHING:
0cfe39
+        return false;
0cfe39
+    default:
0cfe39
+        Q_UNREACHABLE();
0cfe39
+        return false;
0cfe39
+    }
0cfe39
+}
0cfe39
+
0cfe39
+#endif
0cfe39
+
0cfe39
 }
0cfe39
 
0cfe39
 QT_END_NAMESPACE
0cfe39
diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h
0cfe39
index 10132dfe..50db6344 100644
0cfe39
--- a/src/client/qwaylandinputcontext_p.h
0cfe39
+++ b/src/client/qwaylandinputcontext_p.h
0cfe39
@@ -61,6 +61,10 @@
0cfe39
 
0cfe39
 #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
0cfe39
 #include <qwaylandinputmethodeventbuilder_p.h>
0cfe39
+#include <qtwaylandclientglobal_p.h>
0cfe39
+#if QT_CONFIG(xkbcommon)
0cfe39
+#include <xkbcommon/xkbcommon-compose.h>
0cfe39
+#endif
0cfe39
 
0cfe39
 struct wl_callback;
0cfe39
 struct wl_callback_listener;
0cfe39
@@ -155,11 +159,28 @@ public:
0cfe39
 
0cfe39
     void setFocusObject(QObject *object) override;
0cfe39
 
0cfe39
+#if QT_CONFIG(xkbcommon)
0cfe39
+    bool filterEvent(const QEvent *event) override;
0cfe39
+
0cfe39
+    // This invokable is called from QXkbCommon::setXkbContext().
0cfe39
+    Q_INVOKABLE void setXkbContext(struct xkb_context *context) { m_XkbContext = context; }
0cfe39
+#endif
0cfe39
+
0cfe39
 private:
0cfe39
     QWaylandTextInput *textInput() const;
0cfe39
 
0cfe39
     QWaylandDisplay *mDisplay = nullptr;
0cfe39
     QPointer<QWindow> mCurrentWindow;
0cfe39
+
0cfe39
+#if QT_CONFIG(xkbcommon)
0cfe39
+    void ensureInitialized();
0cfe39
+
0cfe39
+    bool m_initialized = false;
0cfe39
+    QObject *m_focusObject = nullptr;
0cfe39
+    xkb_compose_table *m_composeTable = nullptr;
0cfe39
+    xkb_compose_state *m_composeState = nullptr;
0cfe39
+    struct xkb_context *m_XkbContext = nullptr;
0cfe39
+#endif
0cfe39
 };
0cfe39
 
0cfe39
 }
0cfe39
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
0cfe39
index ed4a0eb4..ae045f4f 100644
0cfe39
--- a/src/client/qwaylandinputdevice.cpp
0cfe39
+++ b/src/client/qwaylandinputdevice.cpp
0cfe39
@@ -1201,7 +1201,7 @@ void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type
0cfe39
     QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
0cfe39
     bool filtered = false;
0cfe39
 
0cfe39
-    if (inputContext && !mParent->mQDisplay->usingInputContextFromCompositor()) {
0cfe39
+    if (inputContext) {
0cfe39
         QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey,
0cfe39
                         nativeModifiers, text, autorepeat, count);
0cfe39
         event.setTimestamp(timestamp);
0cfe39
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
0cfe39
index 7ad8e05e..c53ccb78 100644
0cfe39
--- a/src/client/qwaylandintegration.cpp
0cfe39
+++ b/src/client/qwaylandintegration.cpp
0cfe39
@@ -474,13 +474,11 @@ void QWaylandIntegration::reconfigureInputContext()
0cfe39
 
0cfe39
 #if QT_CONFIG(xkbcommon)
0cfe39
     QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext());
0cfe39
+    if (QWaylandInputContext* waylandInput = qobject_cast<QWaylandInputContext*>(mInputContext.get())) {
0cfe39
+        waylandInput->setXkbContext(mDisplay->xkbContext());
0cfe39
+    }
0cfe39
 #endif
0cfe39
 
0cfe39
-    // Even if compositor-side input context handling has been requested, we fallback to
0cfe39
-    // client-side handling if compositor does not provide the text-input extension. This
0cfe39
-    // is why we need to check here which input context actually is being used.
0cfe39
-    mDisplay->mUsingInputContextFromCompositor = qobject_cast<QWaylandInputContext *>(mInputContext.data());
0cfe39
-
0cfe39
     qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className();
0cfe39
 }
0cfe39
 
0cfe39
-- 
9fb289
2.35.1
0cfe39