From 8596998cb025a8338c9403f5ef9db5a23f5cc682 Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Mon, 18 Dec 2023 14:57:28 +0800 Subject: [PATCH] Add QT_IM_MODULES env to allows specify multi IM key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like as QT_QPA_PLATFORM, supports specifying multiple keys, and can perform fallback operations to prioritize the use of a certain plug-in. This is useful when using Wayland and XWayland applications at the same time. For an example, we can set "QT_IM_MODULES=wayland;fcitx", and the wayland application will use the wayland input context plugin, the xwayland application will use fcitx, which can't be done without adding a new environment variable, if we specify "QT_IM_MODULE=wayland", the XWayland applications may not be able to use the input method. Fixes: QTBUG-120202 Change-Id: Iac408af241963147747a2fe685f1e27bf9d9ee64 Reviewed-by: Liang Qi Reviewed-by: Tor Arne Vestbø Reviewed-by: David Edmundson --- .../kernel/qplatforminputcontextfactory.cpp | 29 +++++++++++++++++-- .../kernel/qplatforminputcontextfactory_p.h | 3 +- .../android/qandroidplatformintegration.cpp | 6 ++-- .../platforms/cocoa/qcocoaintegration.mm | 6 ++-- .../platforms/wasm/qwasmintegration.cpp | 8 ++--- .../platforms/windows/qwindowsintegration.cpp | 6 ++-- .../uiautomation/qwindowsuiamainprovider.cpp | 3 +- src/plugins/platforms/xcb/qxcbintegration.cpp | 11 +++---- 8 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/gui/kernel/qplatforminputcontextfactory.cpp b/src/gui/kernel/qplatforminputcontextfactory.cpp index 7074de56af..933d990f7c 100644 --- a/src/gui/kernel/qplatforminputcontextfactory.cpp +++ b/src/gui/kernel/qplatforminputcontextfactory.cpp @@ -28,10 +28,33 @@ QStringList QPlatformInputContextFactory::keys() #endif } -QString QPlatformInputContextFactory::requested() +QStringList QPlatformInputContextFactory::requested() { - QByteArray env = qgetenv("QT_IM_MODULE"); - return env.isNull() ? QString() : QString::fromLocal8Bit(env); + QStringList imList; + QByteArray env = qgetenv("QT_IM_MODULES"); + + if (!env.isEmpty()) + imList = QString::fromLocal8Bit(env).split(QChar::fromLatin1(';'), Qt::SkipEmptyParts); + + if (!imList.isEmpty()) + return imList; + + env = qgetenv("QT_IM_MODULE"); + if (!env.isEmpty()) + imList = {QString::fromLocal8Bit(env)}; + + return imList; +} + +QPlatformInputContext *QPlatformInputContextFactory::create(const QStringList& keys) +{ + for (const QString &key : keys) { + auto plugin = create(key); + if (plugin) + return plugin; + } + + return nullptr; } QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key) diff --git a/src/gui/kernel/qplatforminputcontextfactory_p.h b/src/gui/kernel/qplatforminputcontextfactory_p.h index 4968a51a8b..5f5881c508 100644 --- a/src/gui/kernel/qplatforminputcontextfactory_p.h +++ b/src/gui/kernel/qplatforminputcontextfactory_p.h @@ -27,7 +27,8 @@ class Q_GUI_EXPORT QPlatformInputContextFactory { public: static QStringList keys(); - static QString requested(); + static QStringList requested(); + static QPlatformInputContext *create(const QStringList &keys); static QPlatformInputContext *create(const QString &key); static QPlatformInputContext *create(); }; diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index d6d4ded3f2..038fe5172a 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -304,11 +304,11 @@ static bool needsBasicRenderloopWorkaround() void QAndroidPlatformIntegration::initialize() { - const QString icStr = QPlatformInputContextFactory::requested(); - if (icStr.isNull()) + const auto icStrs = QPlatformInputContextFactory::requested(); + if (icStrs.isEmpty()) m_inputContext.reset(new QAndroidInputContext); else - m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); + m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); } bool QAndroidPlatformIntegration::hasCapability(Capability cap) const diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index ed2ab672b6..fce676158a 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -125,9 +125,9 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) #endif mFontDb.reset(new QCoreTextFontDatabaseEngineFactory); - QString icStr = QPlatformInputContextFactory::requested(); - icStr.isNull() ? mInputContext.reset(new QCocoaInputContext) - : mInputContext.reset(QPlatformInputContextFactory::create(icStr)); + auto icStrs = QPlatformInputContextFactory::requested(); + icStrs.isEmpty() ? mInputContext.reset(new QCocoaInputContext) + : mInputContext.reset(QPlatformInputContextFactory::create(icStrs)); initResources(); QMacAutoReleasePool pool; diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index d2e7c8716c..01367bd56b 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -222,12 +222,12 @@ QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLCon void QWasmIntegration::initialize() { - if (qgetenv("QT_IM_MODULE").isEmpty() && touchPoints < 1) + auto icStrs = QPlatformInputContextFactory::requested(); + if (icStrs.isEmpty() && touchPoints < 1) return; - QString icStr = QPlatformInputContextFactory::requested(); - if (!icStr.isNull()) - m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); + if (!icStrs.isEmpty()) + m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); else m_inputContext.reset(new QWasmInputContext()); } diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 01d364b0f0..6415c9ac50 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -256,9 +256,9 @@ QWindowsIntegration::~QWindowsIntegration() void QWindowsIntegration::initialize() { - QString icStr = QPlatformInputContextFactory::requested(); - icStr.isNull() ? d->m_inputContext.reset(new QWindowsInputContext) - : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); + auto icStrs = QPlatformInputContextFactory::requested(); + icStrs.isEmpty() ? d->m_inputContext.reset(new QWindowsInputContext) + : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); } bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index 05b886d2da..e171f4d452 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #if !defined(Q_CC_BOR) && !defined (Q_CC_GNU) #include @@ -434,7 +435,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR // The native OSK should be disabled if the Qt OSK is in use, // or if disabled via application attribute. - static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE"); + static bool imModuleEmpty = QPlatformInputContextFactory::requested().isEmpty(); bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard); // If we want to disable the native OSK auto-showing diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index e76a34fce8..4dafae31e3 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -344,11 +344,12 @@ void QXcbIntegration::initialize() const auto defaultInputContext = "compose"_L1; // Perform everything that may potentially need the event dispatcher (timers, socket // notifiers) here instead of the constructor. - QString icStr = QPlatformInputContextFactory::requested(); - if (icStr.isNull()) - icStr = defaultInputContext; - m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); - if (!m_inputContext && icStr != defaultInputContext && icStr != "none"_L1) + auto icStrs = QPlatformInputContextFactory::requested(); + if (icStrs.isEmpty()) + icStrs = { defaultInputContext }; + m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); + if (!m_inputContext && !icStrs.contains(defaultInputContext) + && icStrs != QStringList{"none"_L1}) m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext)); connection()->keyboard()->initialize();