[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#991055: unblock: qtwayland-opensource-src/5.15.2-3



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package qtwayland-opensource-src 5.15.2-3.

[ Reason ]
It fixes https://bugs.debian.org/990348.

[ Impact ]
Some users will be unable to use dead keys and type accented characters in
Qt applications under Wayland.

[ Tests ]
I have performed the following test which is based on upstream bug
https://bugs.kde.org/show_bug.cgi?id=411729:

1) Install plasma-workspace-wayland.
2) Log into the Plasma (Wayland) session.
3) Add Greek (polytonic) keyboard layout in the systemsettings.
4) Unset QT_IM_MODULE environment variable.
5) Start any Qt application, e.g. kwrite.
6) Try entering combinations with dead keys, e.g. one of ; ' [ ] and ω
   (which is the key where v is in ASCII layout).
7) Expected result: ώ ὼ ῶ ῳ. With old libqt5waylandclient5 you get just ω.

[ Risks ]
The risks are all related to input handling. There are many factors like
various layouts, input methods, on-screen keyboards, etc. It is impossible to
test any combination of these.

However, this patch is applied in both upstream Qt and KDE's Qt patch
collection, and it seems to work fine there. In upstream Qt there was a small
amendment but it is only needed for builds without xkbcommon.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
Upstream bugs:
- https://bugs.kde.org/show_bug.cgi?id=411729
- https://bugs.kde.org/show_bug.cgi?id=405388

Upstream Qt code review:
- https://codereview.qt-project.org/c/qt/qtwayland/+/338196

Cherry-pick to KDE's Qt Patch Collection:
- https://invent.kde.org/qt/qt/qtwayland/-/merge_requests/4

unblock qtwayland-opensource-src/5.15.2-3

--
Dmitry Shachnev
diff -Nru qtwayland-opensource-src-5.15.2/debian/changelog qtwayland-opensource-src-5.15.2/debian/changelog
--- qtwayland-opensource-src-5.15.2/debian/changelog	2020-12-11 11:32:31.000000000 +0300
+++ qtwayland-opensource-src-5.15.2/debian/changelog	2021-07-10 13:22:01.000000000 +0300
@@ -1,3 +1,10 @@
+qtwayland-opensource-src (5.15.2-3) unstable; urgency=medium
+
+  * Backport upstream patch to fix accented and dead key combinations
+    (closes: #990348).
+
+ -- Dmitry Shachnev <mitya57@debian.org>  Sat, 10 Jul 2021 13:22:01 +0300
+
 qtwayland-opensource-src (5.15.2-2) unstable; urgency=medium
 
   * Bump Standards-Version to 4.5.1, no changes needed.
diff -Nru qtwayland-opensource-src-5.15.2/debian/patches/fix_dead_keys.diff qtwayland-opensource-src-5.15.2/debian/patches/fix_dead_keys.diff
--- qtwayland-opensource-src-5.15.2/debian/patches/fix_dead_keys.diff	1970-01-01 03:00:00.000000000 +0300
+++ qtwayland-opensource-src-5.15.2/debian/patches/fix_dead_keys.diff	2021-07-10 13:22:01.000000000 +0300
@@ -0,0 +1,230 @@
+Description: allow QWaylandInputContext to accept composed key combinations
+ At the moment, we are forcing user to choose to either compose or use
+ the text-input channel. This patch brings some of the QComposeInputContext
+ functionality in order to let applications understand dead key
+ combinations like they are supposed to.
+ .
+ Having it in QWaylandInputContext rather than in QWaylandInputDevice
+ should solve the problems 3aedd01271dc4f4a13103d632df224971ab2b6df had
+ with 57c4af2b18c0fb1d266b245a107fa6cb876b9d9e, because we are doing it
+ in the input context rather than before. This way, if the user is
+ overriding the input method (e.g. by setting QT_IM_MODULE), all the key
+ strokes will still be properly forwarded to the module to use.
+ .
+ This in turn allows us to solve https://bugs.kde.org/show_bug.cgi?id=411729
+ and https://bugs.kde.org/show_bug.cgi?id=405388 since we don't need to
+ choose anymore between physical and virual keyboards anymore.
+Origin: upstream, https://code.qt.io/cgit/qt/qtwayland.git/commit/?id=cca1b94190a094b5
+Last-Update: 2021-06-27
+
+--- a/src/client/qwaylanddisplay_p.h
++++ b/src/client/qwaylanddisplay_p.h
+@@ -175,8 +175,6 @@ public:
+     QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
+     QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
+ 
+-    bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; }
+-
+     struct RegistryGlobal {
+         uint32_t id;
+         QString interface;
+@@ -282,7 +280,6 @@ private:
+     QReadWriteLock m_frameQueueLock;
+ 
+     bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull();
+-    bool mUsingInputContextFromCompositor = false;
+ 
+     void registry_global(uint32_t id, const QString &interface, uint32_t version) override;
+     void registry_global_remove(uint32_t id) override;
+--- a/src/client/qwaylandinputcontext.cpp
++++ b/src/client/qwaylandinputcontext.cpp
+@@ -406,6 +406,8 @@ bool QWaylandInputContext::isValid() con
+ void QWaylandInputContext::reset()
+ {
+     qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
++    if (m_composeState)
++        xkb_compose_state_reset(m_composeState);
+ 
+     QPlatformInputContext::reset();
+ 
+@@ -526,9 +528,14 @@ Qt::LayoutDirection QWaylandInputContext
+     return textInput()->inputDirection();
+ }
+ 
+-void QWaylandInputContext::setFocusObject(QObject *)
++void QWaylandInputContext::setFocusObject(QObject *object)
+ {
+     qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
++#if QT_CONFIG(xkbcommon)
++    m_focusObject = object;
++#else
++    Q_UNUSED(object);
++#endif
+ 
+     if (!textInput())
+         return;
+@@ -561,6 +568,92 @@ QWaylandTextInput *QWaylandInputContext:
+     return mDisplay->defaultInputDevice()->textInput();
+ }
+ 
++#if QT_CONFIG(xkbcommon)
++
++void QWaylandInputContext::ensureInitialized()
++{
++    if (m_initialized)
++        return;
++
++    if (!m_XkbContext) {
++        qCWarning(qLcQpaInputMethods) << "error: xkb context has not been set on" << metaObject()->className();
++        return;
++    }
++
++    m_initialized = true;
++    const char *locale = setlocale(LC_CTYPE, "");
++    if (!locale)
++        locale = setlocale(LC_CTYPE, nullptr);
++    qCDebug(qLcQpaInputMethods) << "detected locale (LC_CTYPE):" << locale;
++
++    m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
++    if (m_composeTable)
++        m_composeState = xkb_compose_state_new(m_composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
++
++    if (!m_composeTable) {
++        qCWarning(qLcQpaInputMethods, "failed to create compose table");
++        return;
++    }
++    if (!m_composeState) {
++        qCWarning(qLcQpaInputMethods, "failed to create compose state");
++        return;
++    }
++}
++
++bool QWaylandInputContext::filterEvent(const QEvent *event)
++{
++    auto keyEvent = static_cast<const QKeyEvent *>(event);
++    if (keyEvent->type() != QEvent::KeyPress)
++        return false;
++
++    if (!inputMethodAccepted())
++        return false;
++
++    // lazy initialization - we don't want to do this on an app startup
++    ensureInitialized();
++
++    if (!m_composeTable || !m_composeState)
++        return false;
++
++    xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey());
++
++    switch (xkb_compose_state_get_status(m_composeState)) {
++    case XKB_COMPOSE_COMPOSING:
++        return true;
++    case XKB_COMPOSE_CANCELLED:
++        reset();
++        return false;
++    case XKB_COMPOSE_COMPOSED:
++    {
++        const int size = xkb_compose_state_get_utf8(m_composeState, nullptr, 0);
++        QVarLengthArray<char, 32> buffer(size + 1);
++        xkb_compose_state_get_utf8(m_composeState, buffer.data(), buffer.size());
++        QString composedText = QString::fromUtf8(buffer.constData());
++
++        QInputMethodEvent event;
++        event.setCommitString(composedText);
++
++        if (!m_focusObject && qApp)
++            m_focusObject = qApp->focusObject();
++
++        if (m_focusObject)
++            QCoreApplication::sendEvent(m_focusObject, &event);
++        else
++            qCWarning(qLcQpaInputMethods, "no focus object");
++
++        reset();
++        return true;
++    }
++    case XKB_COMPOSE_NOTHING:
++        return false;
++    default:
++        Q_UNREACHABLE();
++        return false;
++    }
++}
++
++#endif
++
+ }
+ 
+ QT_END_NAMESPACE
+--- a/src/client/qwaylandinputcontext_p.h
++++ b/src/client/qwaylandinputcontext_p.h
+@@ -61,6 +61,10 @@
+ 
+ #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+ #include <qwaylandinputmethodeventbuilder_p.h>
++#include <qtwaylandclientglobal_p.h>
++#if QT_CONFIG(xkbcommon)
++#include <xkbcommon/xkbcommon-compose.h>
++#endif
+ 
+ struct wl_callback;
+ struct wl_callback_listener;
+@@ -155,11 +159,28 @@ public:
+ 
+     void setFocusObject(QObject *object) override;
+ 
++#if QT_CONFIG(xkbcommon)
++    bool filterEvent(const QEvent *event) override;
++
++    // This invokable is called from QXkbCommon::setXkbContext().
++    Q_INVOKABLE void setXkbContext(struct xkb_context *context) { m_XkbContext = context; }
++#endif
++
+ private:
+     QWaylandTextInput *textInput() const;
+ 
+     QWaylandDisplay *mDisplay = nullptr;
+     QPointer<QWindow> mCurrentWindow;
++
++#if QT_CONFIG(xkbcommon)
++    void ensureInitialized();
++
++    bool m_initialized = false;
++    QObject *m_focusObject = nullptr;
++    xkb_compose_table *m_composeTable = nullptr;
++    xkb_compose_state *m_composeState = nullptr;
++    struct xkb_context *m_XkbContext = nullptr;
++#endif
+ };
+ 
+ }
+--- a/src/client/qwaylandinputdevice.cpp
++++ b/src/client/qwaylandinputdevice.cpp
+@@ -1201,7 +1201,7 @@ void QWaylandInputDevice::Keyboard::hand
+     QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
+     bool filtered = false;
+ 
+-    if (inputContext && !mParent->mQDisplay->usingInputContextFromCompositor()) {
++    if (inputContext) {
+         QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey,
+                         nativeModifiers, text, autorepeat, count);
+         event.setTimestamp(timestamp);
+--- a/src/client/qwaylandintegration.cpp
++++ b/src/client/qwaylandintegration.cpp
+@@ -474,13 +474,11 @@ void QWaylandIntegration::reconfigureInp
+ 
+ #if QT_CONFIG(xkbcommon)
+     QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext());
++    if (QWaylandInputContext* waylandInput = qobject_cast<QWaylandInputContext*>(mInputContext.get())) {
++        waylandInput->setXkbContext(mDisplay->xkbContext());
++    }
+ #endif
+ 
+-    // Even if compositor-side input context handling has been requested, we fallback to
+-    // client-side handling if compositor does not provide the text-input extension. This
+-    // is why we need to check here which input context actually is being used.
+-    mDisplay->mUsingInputContextFromCompositor = qobject_cast<QWaylandInputContext *>(mInputContext.data());
+-
+     qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className();
+ }
+ 
diff -Nru qtwayland-opensource-src-5.15.2/debian/patches/series qtwayland-opensource-src-5.15.2/debian/patches/series
--- qtwayland-opensource-src-5.15.2/debian/patches/series	2020-12-11 11:32:31.000000000 +0300
+++ qtwayland-opensource-src-5.15.2/debian/patches/series	2021-07-10 13:22:01.000000000 +0300
@@ -1 +1,2 @@
 skip_animated_cursor_test.diff
+fix_dead_keys.diff

Attachment: signature.asc
Description: PGP signature


Reply to: