summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/client/qwaylanddisplay_p.h3
-rw-r--r--src/client/qwaylandinputcontext.cpp95
-rw-r--r--src/client/qwaylandinputcontext_p.h21
-rw-r--r--src/client/qwaylandinputdevice.cpp2
-rw-r--r--src/client/qwaylandintegration.cpp8
5 files changed, 119 insertions, 10 deletions
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index 6443cff2b..91715a144 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -178,8 +178,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;
@@ -299,7 +297,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;
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
index 6c586960e..16e03ea17 100644
--- a/src/client/qwaylandinputcontext.cpp
+++ b/src/client/qwaylandinputcontext.cpp
@@ -408,6 +408,8 @@ bool QWaylandInputContext::isValid() const
void QWaylandInputContext::reset()
{
qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
+ if (m_composeState)
+ xkb_compose_state_reset(m_composeState);
QPlatformInputContext::reset();
@@ -528,9 +530,14 @@ Qt::LayoutDirection QWaylandInputContext::inputDirection() const
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;
@@ -563,6 +570,92 @@ QWaylandTextInput *QWaylandInputContext::textInput() const
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
diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h
index df91856c4..912c68ad4 100644
--- a/src/client/qwaylandinputcontext_p.h
+++ b/src/client/qwaylandinputcontext_p.h
@@ -62,6 +62,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;
@@ -156,11 +160,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
};
}
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 420194733..4a046a134 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -1213,7 +1213,7 @@ void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type
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);
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index 7e7567aa8..09de7dddd 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -500,13 +500,11 @@ void QWaylandIntegration::reconfigureInputContext()
#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();
}