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

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