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

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