summaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
authorInho Lee <inho.lee@qt.io>2021-06-19 06:27:22 +0200
committerInho Lee <inho.lee@qt.io>2021-11-19 10:33:31 +0100
commit0cec50bece2dcd69127f19c87a3062e4b13f3723 (patch)
tree8ec11248518b1edfede9538a07ee5e0b0aa5624e /src/client
parent6f195a592b26ad8416a6f02d6bd7258ab3fadf65 (diff)
Support text-input-unstable-v4-wip
This feature can be enabled by -feature-wayland-text-input-v4-wip. It is disabled by default. TextInputManagerV4 is available in a compositor. zwp_text_input_v4 is available for QT_WAYLAND_TEXT_INPUT_PROTOCOL in a client It supports Hangul(Korean) with a qtvirtualkeyboard patchset (refs/changes/02/357902/3) It includes some workarounds for ibus because each ibus module has its own policy for focus-in/focus-out. enter/leave will synchronize with enable/disable and they will happen whenever focus-in/focus-out happen. Cursor/anchor positions are byte offsets. Surrounding text will be trimmed when it is over 4000 byte. For debugging, uses "qt.waylandcompositor.textinput" in a compositor side uses "qt.qpa.wayland.textinput" in a client side Tested on qtvirtualkeyboard and ibus TODO : * QTBUG-97248 - event:preedit_commit_mode is not implemented yet. Current preedit_commit_mode is 'commit'. * request:set_text_change_cause is not implemented. Task-number: QTBUG-94327 Change-Id: I72644893f40f30c4b03cd6a7d05483d12bde1070 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
Diffstat (limited to 'src/client')
-rw-r--r--src/client/CMakeLists.txt8
-rw-r--r--src/client/qwaylanddisplay.cpp47
-rw-r--r--src/client/qwaylanddisplay_p.h7
-rw-r--r--src/client/qwaylandinputcontext.cpp6
-rw-r--r--src/client/qwaylandinputdevice.cpp12
-rw-r--r--src/client/qwaylandintegration.cpp6
-rw-r--r--src/client/qwaylandtextinputinterface_p.h5
-rw-r--r--src/client/qwaylandtextinputv4.cpp409
-rw-r--r--src/client/qwaylandtextinputv4_p.h139
9 files changed, 627 insertions, 12 deletions
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index 79a070332..c7264d677 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -76,6 +76,7 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/pointer-gestures-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/tablet-unstable-v2.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v2.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v4-wip.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wayland.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wp-primary-selection-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-output-unstable-v1.xml
@@ -105,6 +106,13 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient
# )
# special case end
+qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_wayland_text_input_v4_wip
+ SOURCES
+ qwaylandtextinputv4.cpp qwaylandtextinputv4_p.h
+ DEFINES
+ QT_WAYLAND_TEXT_INPUT_V4_WIP=1
+)
+
qt_internal_extend_target(WaylandClient CONDITION QT_FEATURE_clipboard
SOURCES
qwaylandclipboard.cpp qwaylandclipboard_p.h
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 523de1f3c..8043115af 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -61,6 +61,9 @@
#endif
#include "qwaylandhardwareintegration_p.h"
#include "qwaylandtextinputv2_p.h"
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include "qwaylandtextinputv4_p.h"
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include "qwaylandinputcontext_p.h"
#include "qwaylandinputmethodcontext_p.h"
@@ -76,6 +79,7 @@
#include "qwaylandqtkey_p.h"
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+#include <QtWaylandClient/private/qwayland-text-input-unstable-v4-wip.h>
#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h>
@@ -450,6 +454,10 @@ void QWaylandDisplay::checkTextInputProtocol()
<< QLatin1String(QtWayland::zwp_text_input_v2::interface()->name);
timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
<< QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name);
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ tips << QLatin1String(QtWayland::zwp_text_input_v4::interface()->name);
+ timps << QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name);
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
@@ -528,7 +536,10 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
&& (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
if (mTextInputManagerIndex < INT_MAX) {
- mTextInputManager.reset();
+ mTextInputManagerv2.reset();
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ mTextInputManagerv4.reset();
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInput(nullptr);
}
@@ -543,15 +554,35 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
if (mTextInputManagerIndex < INT_MAX) {
mTextInputMethodManager.reset();
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ mTextInputManagerv4.reset();
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInputMethod(nullptr);
}
- mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
+ mTextInputManagerv2.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
- inputDevice->setTextInput(new QWaylandTextInputv2(this, mTextInputManager->get_text_input(inputDevice->wl_seat())));
+ inputDevice->setTextInput(new QWaylandTextInputv2(this, mTextInputManagerv2->get_text_input(inputDevice->wl_seat())));
mWaylandIntegration->reconfigureInputContext();
mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name)
+ && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
+ qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v4";
+ if (mTextInputManagerIndex < INT_MAX) {
+ mTextInputMethodManager.reset();
+ mTextInputManagerv2.reset();
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInputMethod(nullptr);
+ }
+
+ mTextInputManagerv4.reset(new QtWayland::zwp_text_input_manager_v4(registry, id, 1));
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInput(new QWaylandTextInputv4(this, mTextInputManagerv4->get_text_input(inputDevice->wl_seat())));
+ mWaylandIntegration->reconfigureInputContext();
+ mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
} else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) {
bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
if (!disableHardwareIntegration) {
@@ -599,11 +630,19 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) {
- mTextInputManager.reset();
+ mTextInputManagerv2.reset();
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInput(nullptr);
+ mWaylandIntegration->reconfigureInputContext();
+ }
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name)) {
+ mTextInputManagerv4.reset();
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInput(nullptr);
mWaylandIntegration->reconfigureInputContext();
}
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) {
mTextInputMethodManager.reset();
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index b58099ee8..8be911188 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -83,6 +83,7 @@ class QPlatformPlaceholderScreen;
namespace QtWayland {
class qt_surface_extension;
class zwp_text_input_manager_v2;
+ class zwp_text_input_manager_v4;
class qt_text_input_method_manager_v1;
}
@@ -171,7 +172,8 @@ public:
QWaylandPointerGestures *pointerGestures() const { return mPointerGestures.data(); }
QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); }
QtWayland::qt_text_input_method_manager_v1 *textInputMethodManager() const { return mTextInputMethodManager.data(); }
- QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); }
+ QtWayland::zwp_text_input_manager_v2 *textInputManagerv2() const { return mTextInputManagerv2.data(); }
+ QtWayland::zwp_text_input_manager_v4 *textInputManagerv4() const { return mTextInputManagerv4.data(); }
QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
@@ -280,7 +282,8 @@ private:
QScopedPointer<QWaylandPrimarySelectionDeviceManagerV1> mPrimarySelectionManager;
#endif
QScopedPointer<QtWayland::qt_text_input_method_manager_v1> mTextInputMethodManager;
- QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager;
+ QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManagerv2;
+ QScopedPointer<QtWayland::zwp_text_input_manager_v4> mTextInputManagerv4;
QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration;
QScopedPointer<QWaylandXdgOutputManagerV1> mXdgOutputManager;
int mFd = -1;
diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp
index 9ac1c3df6..c59485277 100644
--- a/src/client/qwaylandinputcontext.cpp
+++ b/src/client/qwaylandinputcontext.cpp
@@ -70,7 +70,11 @@ QWaylandInputContext::~QWaylandInputContext()
bool QWaylandInputContext::isValid() const
{
- return mDisplay->textInputManager() != nullptr;
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ return mDisplay->textInputManagerv2() != nullptr || mDisplay->textInputManagerv4() != nullptr;
+#else // QT_WAYLAND_TEXT_INPUT_V4_WIP
+ return mDisplay->textInputManagerv2() != nullptr;
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
}
void QWaylandInputContext::reset()
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 5fbdd2418..7f503ee1e 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -58,6 +58,9 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandshmbackingstore_p.h"
#include "qwaylandtextinputv2_p.h"
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+#include "qwaylandtextinputv4_p.h"
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
#include "qwaylandtextinputinterface_p.h"
#include "qwaylandinputcontext_p.h"
#include "qwaylandinputmethodcontext_p.h"
@@ -421,8 +424,13 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
setPrimarySelectionDevice(psm->createDevice(this));
#endif
- if (mQDisplay->textInputManager())
- mTextInput.reset(new QWaylandTextInputv2(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
+ if (mQDisplay->textInputManagerv2())
+ mTextInput.reset(new QWaylandTextInputv2(mQDisplay, mQDisplay->textInputManagerv2()->get_text_input(wl_seat())));
+
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ if (mQDisplay->textInputManagerv4())
+ mTextInput.reset(new QWaylandTextInputv4(mQDisplay, mQDisplay->textInputManagerv4()->get_text_input(wl_seat())));
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
if (mQDisplay->textInputMethodManager())
mTextInputMethod.reset(new QWaylandTextInputMethod(mQDisplay, mQDisplay->textInputMethodManager()->get_text_input_method(wl_seat())));
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index 67af87cb2..a1da5ccff 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -497,7 +497,11 @@ void QWaylandIntegration::reconfigureInputContext()
if (requested.isNull()) {
if (mDisplay->textInputMethodManager() != nullptr)
mInputContext.reset(new QWaylandInputMethodContext(mDisplay.data()));
- else if (mDisplay->textInputManager() != nullptr)
+#if QT_WAYLAND_TEXT_INPUT_V4_WIP
+ else if (mDisplay->textInputManagerv2() != nullptr || mDisplay->textInputManagerv4() != nullptr)
+#else // QT_WAYLAND_TEXT_INPUT_V4_WIP
+ else if (mDisplay->textInputManagerv2() != nullptr)
+#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
mInputContext.reset(new QWaylandInputContext(mDisplay.data()));
} else {
mInputContext.reset(QPlatformInputContextFactory::create(requested));
diff --git a/src/client/qwaylandtextinputinterface_p.h b/src/client/qwaylandtextinputinterface_p.h
index d918f0835..2b0f381b4 100644
--- a/src/client/qwaylandtextinputinterface_p.h
+++ b/src/client/qwaylandtextinputinterface_p.h
@@ -39,6 +39,7 @@
#ifndef QWAYLANDTEXTINPUTINTERFACE_P_H
#define QWAYLANDTEXTINPUTINTERFACE_P_H
+
//
// W A R N I N G
// -------------
@@ -68,8 +69,8 @@ public:
virtual void disableSurface(::wl_surface *surface) = 0;
virtual void enableSurface(::wl_surface *surface) = 0;
virtual void updateState(Qt::InputMethodQueries queries, uint32_t flags) = 0;
- virtual void showInputPanel() = 0;
- virtual void hideInputPanel() = 0;
+ virtual void showInputPanel() {}
+ virtual void hideInputPanel() {}
virtual bool isInputPanelVisible() const = 0;
virtual QRectF keyboardRect() const = 0;
virtual QLocale locale() const = 0;
diff --git a/src/client/qwaylandtextinputv4.cpp b/src/client/qwaylandtextinputv4.cpp
new file mode 100644
index 000000000..5cecaf7a4
--- /dev/null
+++ b/src/client/qwaylandtextinputv4.cpp
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandtextinputv4_p.h"
+
+#include "qwaylandwindow_p.h"
+#include "qwaylandinputmethodeventbuilder_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qwindow.h>
+#include <QTextCharFormat>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qLcQpaWaylandTextInput, "qt.qpa.wayland.textinput")
+
+namespace QtWaylandClient {
+
+QWaylandTextInputv4::QWaylandTextInputv4(QWaylandDisplay *display,
+ struct ::zwp_text_input_v4 *text_input)
+ : QtWayland::zwp_text_input_v4(text_input)
+ , m_display(display)
+{
+
+}
+
+QWaylandTextInputv4::~QWaylandTextInputv4()
+{
+}
+
+namespace {
+const Qt::InputMethodQueries supportedQueries = Qt::ImEnabled |
+ Qt::ImSurroundingText |
+ Qt::ImCursorPosition |
+ Qt::ImAnchorPosition |
+ Qt::ImHints |
+ Qt::ImCursorRectangle;
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_enter(struct ::wl_surface *surface)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ m_surface = surface;
+
+ m_pendingPreeditString.clear();
+ m_pendingCommitString.clear();
+ m_pendingDeleteBeforeText = 0;
+ m_pendingDeleteAfterText = 0;
+
+ enable();
+ updateState(supportedQueries, update_state_enter);
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_leave(struct ::wl_surface *surface)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ if (m_surface != surface) {
+ qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "focused surface" << m_surface;
+ return;
+ }
+
+ // QTBUG-97248: check commit_mode
+ // Currently text-input-unstable-v4-wip is implemented with preedit_commit_mode
+ // 'commit'
+
+ m_currentPreeditString.clear();
+
+ m_surface = nullptr;
+ m_currentSerial = 0U;
+
+ disable();
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Done";
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_preedit_string(const QString &text, int32_t cursorBegin, int32_t cursorEnd)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text << cursorBegin << cursorEnd;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ m_pendingPreeditString.text = text;
+ m_pendingPreeditString.cursorBegin = cursorBegin;
+ m_pendingPreeditString.cursorEnd = cursorEnd;
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_commit_string(const QString &text)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ m_pendingCommitString = text;
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_delete_surrounding_text(uint32_t beforeText, uint32_t afterText)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << beforeText << afterText;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ m_pendingDeleteBeforeText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, beforeText);
+ m_pendingDeleteAfterText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, afterText);
+}
+
+void QWaylandTextInputv4::zwp_text_input_v4_done(uint32_t serial)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << serial << m_currentSerial;
+
+ // This is a case of double click.
+ // text_input_v4 will ignore this done signal and just keep the selection of the clicked word.
+ if (m_cursorPos != m_anchorPos && (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)) {
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Ignore done";
+ m_pendingDeleteBeforeText = 0;
+ m_pendingDeleteAfterText = 0;
+ m_pendingPreeditString.clear();
+ m_pendingCommitString.clear();
+ return;
+ }
+
+ QObject *focusObject = QGuiApplication::focusObject();
+ if (!focusObject)
+ return;
+
+ if (!m_surface) {
+ qCWarning(qLcQpaWaylandTextInput) << Q_FUNC_INFO << serial << "Surface is not enabled yet";
+ return;
+ }
+
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "PREEDIT" << m_pendingPreeditString.text << m_pendingPreeditString.cursorBegin;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ {
+ if (m_pendingPreeditString.cursorBegin != -1 ||
+ m_pendingPreeditString.cursorEnd != -1) {
+ // Current supported cursor shape is just line.
+ // It means, cursorEnd and cursorBegin are the same.
+ QInputMethodEvent::Attribute attribute1(QInputMethodEvent::Cursor,
+ m_pendingPreeditString.text.length(),
+ 1);
+ attributes.append(attribute1);
+ }
+
+ // only use single underline style for now
+ QTextCharFormat format;
+ format.setFontUnderline(true);
+ format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+ QInputMethodEvent::Attribute attribute2(QInputMethodEvent::TextFormat,
+ 0,
+ m_pendingPreeditString.text.length(), format);
+ attributes.append(attribute2);
+ }
+ QInputMethodEvent event(m_pendingPreeditString.text, attributes);
+
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "DELETE" << m_pendingDeleteBeforeText << m_pendingDeleteAfterText;
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "COMMIT" << m_pendingCommitString;
+
+ // A workaround for reselection
+ // It will disable redundant commit after reselection
+ if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)
+ m_condReselection = true;
+
+ event.setCommitString(m_pendingCommitString,
+ -m_pendingDeleteBeforeText,
+ m_pendingDeleteBeforeText + m_pendingDeleteAfterText);
+ m_currentPreeditString = m_pendingPreeditString;
+ m_pendingPreeditString.clear();
+ m_pendingCommitString.clear();
+ m_pendingDeleteBeforeText = 0;
+ m_pendingDeleteAfterText = 0;
+ QCoreApplication::sendEvent(focusObject, &event);
+
+ if (serial == m_currentSerial)
+ updateState(supportedQueries, update_state_full);
+}
+
+void QWaylandTextInputv4::reset()
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ m_pendingPreeditString.clear();
+}
+
+void QWaylandTextInputv4::enableSurface(::wl_surface *)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+}
+
+void QWaylandTextInputv4::disableSurface(::wl_surface *surface)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+
+ if (m_surface != surface) {
+ qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "for surface" << surface << "focused surface" << m_surface;
+ return;
+ }
+}
+
+void QWaylandTextInputv4::commit()
+{
+ m_currentSerial = (m_currentSerial < UINT_MAX) ? m_currentSerial + 1U: 0U;
+
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << m_currentSerial;
+ QtWayland::zwp_text_input_v4::commit();
+}
+
+void QWaylandTextInputv4::updateState(Qt::InputMethodQueries queries, uint32_t flags)
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << queries << flags;
+
+ if (!QGuiApplication::focusObject())
+ return;
+
+ if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
+ return;
+
+ auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
+ auto *surface = window->wlSurface();
+ if (!surface || (surface != m_surface))
+ return;
+
+ queries &= supportedQueries;
+ bool needsCommit = false;
+
+ QInputMethodQueryEvent event(queries);
+ QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
+
+ // For some reason, a query for Qt::ImSurroundingText gives an empty string even though it is not.
+ if (!(queries & Qt::ImSurroundingText) && event.value(Qt::ImSurroundingText).toString().isEmpty()) {
+ return;
+ }
+
+ if (queries & Qt::ImCursorRectangle) {
+ const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
+ const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
+ const QMargins margins = window->frameMargins();
+ const QRect &surfaceRect = windowRect.translated(margins.left(), margins.top());
+ if (surfaceRect != m_cursorRect) {
+ set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
+ m_cursorRect = surfaceRect;
+ needsCommit = true;
+ }
+ }
+
+ if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) {
+ QString text = event.value(Qt::ImSurroundingText).toString();
+ int cursor = event.value(Qt::ImCursorPosition).toInt();
+ int anchor = event.value(Qt::ImAnchorPosition).toInt();
+
+ qCDebug(qLcQpaWaylandTextInput) << "Orginal surrounding_text from InputMethodQuery: " << text << cursor << anchor;
+
+ // Make sure text is not too big
+ // surround_text cannot exceed 4000byte in wayland protocol
+ // The worst case will be supposed here.
+ const int MAX_MESSAGE_SIZE = 4000;
+
+ if (text.toUtf8().size() > MAX_MESSAGE_SIZE) {
+ const int selectionStart = QWaylandInputMethodEventBuilder::indexToWayland(text, qMin(cursor, anchor));
+ const int selectionEnd = QWaylandInputMethodEventBuilder::indexToWayland(text, qMax(cursor, anchor));
+ const int selectionLength = selectionEnd - selectionStart;
+ // If selection is bigger than 4000 byte, it is fixed to 4000 byte.
+ // anchor will be moved in the 4000 byte boundary.
+ if (selectionLength > MAX_MESSAGE_SIZE) {
+ if (anchor > cursor) {
+ const int length = MAX_MESSAGE_SIZE;
+ anchor = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, length, cursor);
+ anchor -= cursor;
+ text = text.mid(cursor, anchor);
+ cursor = 0;
+ } else {
+ const int length = -MAX_MESSAGE_SIZE;
+ anchor = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, length, cursor);
+ cursor -= anchor;
+ text = text.mid(anchor, cursor);
+ anchor = 0;
+ }
+ } else {
+ const int offset = (MAX_MESSAGE_SIZE - selectionLength) / 2;
+
+ int textStart = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, -offset, qMin(cursor, anchor));
+ int textEnd = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, MAX_MESSAGE_SIZE, textStart);
+
+ anchor -= textStart;
+ cursor -= textStart;
+ text = text.mid(textStart, textEnd - textStart);
+ }
+ }
+ qCDebug(qLcQpaWaylandTextInput) << "Modified surrounding_text: " << text << cursor << anchor;
+
+ const int cursorPos = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor);
+ const int anchorPos = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor);
+
+ if (m_surroundingText != text || m_cursorPos != cursorPos || m_anchorPos != anchorPos) {
+ qCDebug(qLcQpaWaylandTextInput) << "Current surrounding_text: " << m_surroundingText << m_cursorPos << m_anchorPos;
+ qCDebug(qLcQpaWaylandTextInput) << "New surrounding_text: " << text << cursorPos << anchorPos;
+
+ set_surrounding_text(text, cursorPos, anchorPos);
+
+ // A workaround in the case of reselection
+ // It will work when re-clicking a preedit text
+ if (m_condReselection) {
+ qCDebug(qLcQpaWaylandTextInput) << "\"commit\" is disabled when Reselection by changing focus";
+ m_condReselection = false;
+ needsCommit = false;
+
+ }
+
+ m_surroundingText = text;
+ m_cursorPos = cursorPos;
+ m_anchorPos = anchorPos;
+ m_cursor = cursor;
+ }
+ }
+
+ if (queries & Qt::ImHints) {
+ QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convertV4(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt()));
+ qCDebug(qLcQpaWaylandTextInput) << m_contentHint << contentType.hint;
+ qCDebug(qLcQpaWaylandTextInput) << m_contentPurpose << contentType.purpose;
+
+ if (m_contentHint != contentType.hint || m_contentPurpose != contentType.purpose) {
+ qCDebug(qLcQpaWaylandTextInput) << "set_content_type: " << contentType.hint << contentType.purpose;
+ set_content_type(contentType.hint, contentType.purpose);
+
+ m_contentHint = contentType.hint;
+ m_contentPurpose = contentType.purpose;
+ needsCommit = true;
+ }
+ }
+
+ if (needsCommit
+ && (flags == update_state_change || flags == update_state_enter))
+ commit();
+}
+
+void QWaylandTextInputv4::setCursorInsidePreedit(int cursor)
+{
+ Q_UNUSED(cursor);
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support setting cursor inside preedit. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+}
+
+bool QWaylandTextInputv4::isInputPanelVisible() const
+{
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support querying input method visibility. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+ return false;
+}
+
+QRectF QWaylandTextInputv4::keyboardRect() const
+{
+ qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
+ return m_cursorRect;
+}
+
+QLocale QWaylandTextInputv4::locale() const
+{
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support querying input language. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+ return QLocale();
+}
+
+Qt::LayoutDirection QWaylandTextInputv4::inputDirection() const
+{
+ qCWarning(qLcQpaWaylandTextInput) << "QWaylandTextInputV4: Input protocol \"text-input-unstable-v4-wip\" does not support querying input direction. Use qt-text-input-method-unstable-v1 instead for full support of Qt input method events.";
+ return Qt::LeftToRight;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/client/qwaylandtextinputv4_p.h b/src/client/qwaylandtextinputv4_p.h
new file mode 100644
index 000000000..d273cef27
--- /dev/null
+++ b/src/client/qwaylandtextinputv4_p.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDTEXTINPUTV4_P_H
+#define QWAYLANDTEXTINPUTV4_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwaylandtextinputinterface_p.h"
+#include <QtWaylandClient/private/qwayland-text-input-unstable-v4-wip.h>
+#include <qwaylandinputmethodeventbuilder_p.h>
+
+struct wl_callback;
+struct wl_callback_listener;
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcQpaWaylandTextInput)
+
+namespace QtWaylandClient {
+
+class QWaylandDisplay;
+
+class QWaylandTextInputv4 : public QtWayland::zwp_text_input_v4, public QWaylandTextInputInterface
+{
+public:
+ QWaylandTextInputv4(QWaylandDisplay *display, struct ::zwp_text_input_v4 *text_input);
+ ~QWaylandTextInputv4() override;
+
+ void reset() override;
+ void commit() override;
+ void updateState(Qt::InputMethodQueries queries, uint32_t flags) override;
+ // TODO: not supported yet
+ void setCursorInsidePreedit(int cursor) override;
+
+ bool isInputPanelVisible() const override;
+ QRectF keyboardRect() const override;
+
+ QLocale locale() const override;
+ Qt::LayoutDirection inputDirection() const override;
+
+ void enableSurface(::wl_surface *surface) override;
+ void disableSurface(::wl_surface *surface) override;
+
+protected:
+ void zwp_text_input_v4_enter(struct ::wl_surface *surface) override;
+ void zwp_text_input_v4_leave(struct ::wl_surface *surface) override;
+ void zwp_text_input_v4_preedit_string(const QString &text, int32_t cursor_begin, int32_t cursor_end) override;
+ void zwp_text_input_v4_commit_string(const QString &text) override;
+ void zwp_text_input_v4_delete_surrounding_text(uint32_t before_length, uint32_t after_length) override;
+ void zwp_text_input_v4_done(uint32_t serial) override;
+
+private:
+ QWaylandDisplay *m_display;
+ QWaylandInputMethodEventBuilder m_builder;
+
+ ::wl_surface *m_surface = nullptr; // ### Here for debugging purposes
+
+ struct PreeditInfo {
+ QString text;
+ int cursorBegin = 0;
+ int cursorEnd = 0;
+
+ void clear() {
+ text.clear();
+ cursorBegin = 0;
+ cursorEnd = 0;
+ }
+ };
+
+ PreeditInfo m_pendingPreeditString;
+ PreeditInfo m_currentPreeditString;
+ QString m_pendingCommitString;
+ uint m_pendingDeleteBeforeText = 0;
+ uint m_pendingDeleteAfterText = 0;
+
+ QString m_surroundingText;
+ int m_cursor; // cursor position in QString
+ int m_cursorPos; // cursor position in wayland index
+ int m_anchorPos; // anchor position in wayland index
+ uint32_t m_contentHint = 0;
+ uint32_t m_contentPurpose = 0;
+ QRect m_cursorRect;
+
+ uint m_currentSerial = 0;
+
+ bool m_condReselection = false;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDTEXTINPUTV4_P_H