diff --git a/src/3rdparty/wayland/extensions/qt-text-input-method-unstable-v1.xml b/src/3rdparty/wayland/extensions/qt-text-input-method-unstable-v1.xml
new file mode 100644
index 00000000000..2e8cd4ec3a6
--- /dev/null
+++ b/src/3rdparty/wayland/extensions/qt-text-input-method-unstable-v1.xml
@@ -0,0 +1,301 @@
+
+
+
+
+ Copyright © 2020 The Qt Company Ltd.
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+
+ The qt_text_input_method interface represents input method events
+ associated with a seat, and is intended to exactly match the
+ internal events of the Qt framework.
+
+
+
+
+ Destroy the qt_text_input_method object.
+
+
+
+
+
+ Enable text input in a surface (usually when a text entry inside of it
+ has focus).
+
+ This can be called before or after a surface gets text (or keyboard)
+ focus via the enter event. Text input to a surface is only active
+ when it has the current text (or keyboard) focus and is enabled.
+
+
+
+
+
+
+ Disable text input in a surface (typically when there is no focus on any
+ text entry inside the surface).
+
+
+
+
+
+
+ Notification that this seat's text-input focus is on a certain surface.
+
+ When the seat has the keyboard capability the text-input focus follows
+ the keyboard focus.
+
+
+
+
+
+
+ Notification that this seat's text-input focus is no longer on
+ a certain surface.
+
+ The leave notification is sent before the enter notification
+ for the new focus.
+
+ When the seat has the keyboard capability the text-input focus follows
+ the keyboard focus.
+
+
+
+
+
+
+ A QKeyEvent has been sent from the input method.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Starts an input method event. This can be followed by
+ any number of input_method_event_attribute events
+ and will always be finished by an end_input_method_event.
+
+
+
+
+
+
+
+ Appends an attribute to the input method event with
+ the given serial. Must be preceded by a start_input_method_event
+ and concluded by a an end_input_method_event. See documentation
+ of QInputMethodEvent for details on the attributes.
+
+
+
+
+
+
+
+
+
+
+ Concludes a previously started input method event. Together with
+ the preceding input_method_event_attribute events with the same
+ serial, this should be converted into a QInputMethodEvent.
+
+
+
+
+
+
+
+
+
+
+ Event to notify client that the visibility of the input method has
+ been changed.
+
+
+
+
+
+
+ Event to notify client that the keyboard rectangle of the input method
+ has been changed.
+
+
+
+
+
+
+
+
+
+ Event to notify client that the locale of the input method
+ has been changed.
+
+
+
+
+
+
+ Event to notify client that the input direction of the input method
+ has been changed.
+
+
+
+
+
+
+ Request for the input method to reset. Corresponds to QInputMethod::reset().
+
+
+
+
+
+ Request for the input method to commit its current content. Corresponds to QInputMethod::commit().
+
+
+
+
+
+ Passes a mouse click or context menu request from the client to the server. Corresponds to QInputMethod::invokeAction().
+
+
+
+
+
+
+
+ Notifies the server of the client's current input method hints.
+
+
+
+
+
+
+ Notifies the server of the client's current cursor rectangle.
+
+
+
+
+
+
+
+
+
+ Notifies the server of the client's current cursor position.
+
+
+
+
+
+
+ Notifies the server of the client's current surrounding text and its offset in the
+ complete text.
+
+
+
+
+
+
+
+ Notifies the server of the client's current anchor position.
+
+
+
+
+
+
+ Notifies the server of the client's current absolute cursor position.
+
+
+
+
+
+
+ Notifies the server of the client's current preferred language.
+
+
+
+
+
+
+ Starts an update sequence to notify the server that the client's state has
+ changed. This is followed by any number of update requests for specific
+ parts of the state and concluded by an end_update request.
+
+
+
+
+
+
+ Concludes the previously started update request.
+
+
+
+
+
+ Requests that the input panel of the input method is visible.
+
+
+
+
+
+ Requests that the input panel of the input method is not visible.
+
+
+
+
+
+ Sent on receipt of an end_input_method_event to acknowledge that
+ the client has received and handled the event.
+
+
+
+
+
+
+ Manages qt_text_input_method objects.
+
+
+
+
+ Destroy the qt_text_input_method_manager object.
+
+
+
+
+
+ Creates a new text-input-method object for a given seat.
+
+
+
+
+
+
diff --git a/src/plugins/platforms/wayland/.prev_CMakeLists.txt b/src/plugins/platforms/wayland/.prev_CMakeLists.txt
index ac323a56f3f..454b06ae38f 100644
--- a/src/plugins/platforms/wayland/.prev_CMakeLists.txt
+++ b/src/plugins/platforms/wayland/.prev_CMakeLists.txt
@@ -30,6 +30,7 @@ qt_add_module(WaylandClient
qwaylandextendedsurface.cpp qwaylandextendedsurface_p.h
qwaylandinputcontext.cpp qwaylandinputcontext_p.h
qwaylandinputdevice.cpp qwaylandinputdevice_p.h
+ qwaylandinputmethodcontext.cpp qwaylandinputmethodcontext_p.h
qwaylandintegration.cpp qwaylandintegration_p.h
qwaylandnativeinterface.cpp qwaylandnativeinterface_p.h
qwaylandqtkey.cpp qwaylandqtkey_p.h
@@ -56,7 +57,6 @@ qt_add_module(WaylandClient
LIBRARIES
Qt::CorePrivate
Qt::GuiPrivate
- Qt::PlatformHeadersPrivate
PUBLIC_LIBRARIES
Qt::Core
Qt::Gui
@@ -74,6 +74,7 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wp-primary-selection-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-output-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-key-unstable-v1.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-text-input-method-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-windowmanager.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/surface-extension.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/touch-extension.xml
diff --git a/src/plugins/platforms/wayland/CMakeLists.txt b/src/plugins/platforms/wayland/CMakeLists.txt
index 88b27b5f3a9..01957eacd7f 100644
--- a/src/plugins/platforms/wayland/CMakeLists.txt
+++ b/src/plugins/platforms/wayland/CMakeLists.txt
@@ -30,6 +30,7 @@ qt_add_module(WaylandClient
qwaylandextendedsurface.cpp qwaylandextendedsurface_p.h
qwaylandinputcontext.cpp qwaylandinputcontext_p.h
qwaylandinputdevice.cpp qwaylandinputdevice_p.h
+ qwaylandinputmethodcontext.cpp qwaylandinputmethodcontext_p.h
qwaylandintegration.cpp qwaylandintegration_p.h
qwaylandnativeinterface.cpp qwaylandnativeinterface_p.h
qwaylandqtkey.cpp qwaylandqtkey_p.h
@@ -73,6 +74,7 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/wp-primary-selection-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/xdg-output-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-key-unstable-v1.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-text-input-method-unstable-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/qt-windowmanager.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/surface-extension.xml
${CMAKE_CURRENT_SOURCE_DIR}/../extensions/touch-extension.xml
diff --git a/src/plugins/platforms/wayland/client.pro b/src/plugins/platforms/wayland/client.pro
index d6df946b58e..da6a926dc48 100644
--- a/src/plugins/platforms/wayland/client.pro
+++ b/src/plugins/platforms/wayland/client.pro
@@ -22,6 +22,7 @@ WAYLANDCLIENTSOURCES += \
../extensions/touch-extension.xml \
../extensions/qt-key-unstable-v1.xml \
../extensions/qt-windowmanager.xml \
+ ../extensions/qt-text-input-method-unstable-v1.xml \
../3rdparty/protocol/wp-primary-selection-unstable-v1.xml \
../3rdparty/protocol/tablet-unstable-v2.xml \
../3rdparty/protocol/text-input-unstable-v2.xml \
@@ -50,6 +51,7 @@ SOURCES += qwaylandintegration.cpp \
qwaylanddecorationplugin.cpp \
qwaylandwindowmanagerintegration.cpp \
qwaylandinputcontext.cpp \
+ qwaylandinputmethodcontext.cpp \
qwaylandshm.cpp \
qwaylandbuffer.cpp \
@@ -74,6 +76,7 @@ HEADERS += qwaylandintegration_p.h \
qwaylanddecorationplugin_p.h \
qwaylandwindowmanagerintegration_p.h \
qwaylandinputcontext_p.h \
+ qwaylandinputmethodcontext_p.h \
qwaylandshm_p.h \
qtwaylandclientglobal.h \
qtwaylandclientglobal_p.h \
diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp
index 9e5359357d8..e39f912809b 100644
--- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp
+++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp
@@ -61,6 +61,7 @@
#endif
#include "qwaylandhardwareintegration_p.h"
#include "qwaylandinputcontext_p.h"
+#include "qwaylandinputmethodcontext_p.h"
#include "qwaylandwindowmanagerintegration_p.h"
#include "qwaylandshellintegration_p.h"
@@ -74,6 +75,7 @@
#include
#include
+#include
#include
@@ -339,6 +341,11 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
} else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
#endif
+ } else if (interface == QStringLiteral("qt_text_input_method_manager_v1") && !mClientSideInputContextRequested) {
+ mTextInputMethodManager.reset(new QtWayland::qt_text_input_method_manager_v1(registry, id, 1));
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInputMethod(new QWaylandTextInputMethod(this, mTextInputMethodManager->get_text_input_method(inputDevice->wl_seat())));
+ mWaylandIntegration->reconfigureInputContext();
} else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
@@ -396,6 +403,12 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
inputDevice->setTextInput(nullptr);
mWaylandIntegration->reconfigureInputContext();
}
+ if (global.interface == QStringLiteral("qt_text_input_method_manager_v1")) {
+ mTextInputMethodManager.reset();
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
+ inputDevice->setTextInputMethod(nullptr);
+ mWaylandIntegration->reconfigureInputContext();
+ }
mGlobals.removeAt(i);
break;
}
diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h
index d386c66ac68..68d72812f52 100644
--- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h
+++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h
@@ -81,6 +81,7 @@ class QPlatformPlaceholderScreen;
namespace QtWayland {
class qt_surface_extension;
class zwp_text_input_manager_v2;
+ class qt_text_input_method_manager_v1;
}
namespace QtWaylandClient {
@@ -163,6 +164,7 @@ public:
QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); }
QWaylandTabletManagerV2 *tabletManager() const { return mTabletManager.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(); }
QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
QWaylandXdgOutputManagerV1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
@@ -268,6 +270,7 @@ private:
#if QT_CONFIG(wayland_client_primary_selection)
QScopedPointer mPrimarySelectionManager;
#endif
+ QScopedPointer mTextInputMethodManager;
QScopedPointer mTextInputManager;
QScopedPointer mHardwareIntegration;
QScopedPointer mXdgOutputManager;
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
index e86274276f0..343ccc8d7e2 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
@@ -57,6 +57,7 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandshmbackingstore_p.h"
#include "qwaylandinputcontext_p.h"
+#include "qwaylandinputmethodcontext_p.h"
#include
#include
@@ -420,6 +421,9 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
if (mQDisplay->textInputManager())
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
+ if (mQDisplay->textInputMethodManager())
+ mTextInputMethod.reset(new QWaylandTextInputMethod(mQDisplay, mQDisplay->textInputMethodManager()->get_text_input_method(wl_seat())));
+
if (auto *tm = mQDisplay->tabletManager())
mTabletSeat.reset(new QWaylandTabletSeatV2(tm, this));
}
@@ -535,11 +539,21 @@ void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
mTextInput.reset(textInput);
}
+void QWaylandInputDevice::setTextInputMethod(QWaylandTextInputMethod *textInputMethod)
+{
+ mTextInputMethod.reset(textInputMethod);
+}
+
QWaylandTextInput *QWaylandInputDevice::textInput() const
{
return mTextInput.data();
}
+QWaylandTextInputMethod *QWaylandInputDevice::textInputMethod() const
+{
+ return mTextInputMethod.data();
+}
+
void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button)
{
if (mPointer)
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
index 31bd3008849..53691e68254 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
@@ -91,6 +91,7 @@ class QWaylandPrimarySelectionDeviceV1;
#endif
class QWaylandTabletSeatV2;
class QWaylandTextInput;
+class QWaylandTextInputMethod;
#if QT_CONFIG(cursor)
class QWaylandCursorTheme;
class CursorSurface;
@@ -134,6 +135,9 @@ public:
void setTextInput(QWaylandTextInput *textInput);
QWaylandTextInput *textInput() const;
+ void setTextInputMethod(QWaylandTextInputMethod *textInputMethod);
+ QWaylandTextInputMethod *textInputMethod() const;
+
void removeMouseButtonFromState(Qt::MouseButton button);
QWaylandWindow *pointerFocus() const;
@@ -187,6 +191,7 @@ private:
Touch *mTouch = nullptr;
QScopedPointer mTextInput;
+ QScopedPointer mTextInputMethod;
QScopedPointer mTabletSeat;
uint32_t mTime = 0;
diff --git a/src/plugins/platforms/wayland/qwaylandinputmethodcontext.cpp b/src/plugins/platforms/wayland/qwaylandinputmethodcontext.cpp
new file mode 100644
index 00000000000..e6ab1da4ff1
--- /dev/null
+++ b/src/plugins/platforms/wayland/qwaylandinputmethodcontext.cpp
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 "qwaylandinputmethodcontext_p.h"
+#include "qwaylanddisplay_p.h"
+#include "qwaylandinputdevice_p.h"
+
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods)
+
+namespace QtWaylandClient {
+
+static constexpr int maxStringSize = 1000; // actual max is 4096/3
+
+QWaylandTextInputMethod::QWaylandTextInputMethod(QWaylandDisplay *display, struct ::qt_text_input_method_v1 *textInputMethod)
+ : QtWayland::qt_text_input_method_v1(textInputMethod)
+ , m_display(display)
+{
+}
+
+QWaylandTextInputMethod::~QWaylandTextInputMethod()
+{
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_visible_changed(int32_t visible)
+{
+ m_isVisible = visible;
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_locale_changed(const QString &localeName)
+{
+ m_locale = QLocale(localeName);
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_input_direction_changed(int32_t inputDirection)
+{
+ m_layoutDirection = Qt::LayoutDirection(inputDirection);
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_keyboard_rectangle_changed(wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height)
+{
+ m_keyboardRect = QRectF(wl_fixed_to_double(x),
+ wl_fixed_to_double(y),
+ wl_fixed_to_double(width),
+ wl_fixed_to_double(height));
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_start_input_method_event(uint32_t serial, int32_t surrounding_text_offset)
+{
+ if (m_pendingInputMethodEvents.contains(serial)) {
+ qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "already started";
+ return;
+ }
+
+ m_pendingInputMethodEvents[serial] = QList{};
+ m_offsetFromCompositor[serial] = surrounding_text_offset;
+}
+
+// We need to keep surrounding text below maxStringSize characters, with cursorPos centered in that substring
+
+static int calculateOffset(const QString &text, int cursorPos)
+{
+ int size = text.size();
+ int halfSize = maxStringSize/2;
+ if (size <= maxStringSize || cursorPos < halfSize)
+ return 0;
+ if (cursorPos > size - halfSize)
+ return size - maxStringSize;
+ return cursorPos - halfSize;
+}
+
+static QString mapSurroundingTextToCompositor(const QString &s, int offset)
+{
+ return s.mid(offset, maxStringSize);
+}
+
+static int mapPositionToCompositor(int pos, int offset)
+{
+ return pos - offset;
+}
+
+static int mapPositionFromCompositor(int pos, int offset)
+{
+ return pos + offset;
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_input_method_event_attribute(uint32_t serial, int32_t type, int32_t start, int32_t length, const QString &value)
+{
+ if (!m_pendingInputMethodEvents.contains(serial)) {
+ qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "does not exist";
+ return;
+ }
+
+ int startMapped = mapPositionFromCompositor(start, m_offsetFromCompositor[serial]);
+ QList &attributes = m_pendingInputMethodEvents[serial];
+ switch (type) {
+ case QInputMethodEvent::Selection:
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), startMapped, length));
+ break;
+ case QInputMethodEvent::Cursor:
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, QColor(value)));
+ break;
+ case QInputMethodEvent::TextFormat:
+ {
+ QTextCharFormat textFormat;
+ textFormat.setProperty(QTextFormat::FontUnderline, true);
+ textFormat.setProperty(QTextFormat::TextUnderlineStyle, QTextCharFormat::SingleUnderline);
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, textFormat));
+ break;
+ }
+ case QInputMethodEvent::Language:
+ case QInputMethodEvent::Ruby:
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, value));
+ break;
+ };
+}
+
+void QWaylandTextInputMethod::sendInputState(QInputMethodQueryEvent *event, Qt::InputMethodQueries queries)
+{
+ int cursorPosition = event->value(Qt::ImCursorPosition).toInt();
+ int anchorPosition = event->value(Qt::ImAnchorPosition).toInt();
+ QString surroundingText = event->value(Qt::ImSurroundingText).toString();
+ int offset = calculateOffset(surroundingText, cursorPosition);
+
+ if (queries & Qt::ImCursorPosition)
+ update_cursor_position(mapPositionToCompositor(cursorPosition, offset));
+ if (queries & Qt::ImSurroundingText)
+ update_surrounding_text(mapSurroundingTextToCompositor(surroundingText, offset), offset);
+ if (queries & Qt::ImAnchorPosition)
+ update_anchor_position(mapPositionToCompositor(anchorPosition, offset));
+ if (queries & Qt::ImAbsolutePosition)
+ update_absolute_position(event->value(Qt::ImAbsolutePosition).toInt()); // do not map: this is the position in the whole document
+}
+
+
+void QWaylandTextInputMethod::text_input_method_v1_end_input_method_event(uint32_t serial, const QString &commitString, const QString &preeditString, int32_t replacementStart, int32_t replacementLength)
+{
+ if (!m_pendingInputMethodEvents.contains(serial)) {
+ qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "does not exist";
+ return;
+ }
+
+ QList attributes = m_pendingInputMethodEvents.take(serial);
+ m_offsetFromCompositor.remove(serial);
+ if (QGuiApplication::focusObject() != nullptr) {
+ QInputMethodEvent event(preeditString, attributes);
+ event.setCommitString(commitString, replacementStart, replacementLength);
+ QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
+ }
+
+ // Send current state to make sure it matches
+ if (QGuiApplication::focusObject() != nullptr) {
+ QInputMethodQueryEvent event(Qt::ImCursorPosition | Qt::ImSurroundingText | Qt::ImAnchorPosition | Qt::ImAbsolutePosition);
+ QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
+ sendInputState(&event);
+ }
+
+ acknowledge_input_method();
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_key(int32_t type,
+ int32_t key,
+ int32_t modifiers,
+ int32_t autoRepeat,
+ int32_t count,
+ int32_t nativeScanCode,
+ int32_t nativeVirtualKey,
+ int32_t nativeModifiers,
+ const QString &text)
+{
+ if (QGuiApplication::focusObject() != nullptr) {
+ QKeyEvent event(QKeyEvent::Type(type),
+ key,
+ Qt::KeyboardModifiers(modifiers),
+ nativeScanCode,
+ nativeVirtualKey,
+ nativeModifiers,
+ text,
+ autoRepeat,
+ count);
+ QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
+ }
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_enter(struct ::wl_surface *surface)
+{
+ m_surface = surface;
+}
+
+void QWaylandTextInputMethod::text_input_method_v1_leave(struct ::wl_surface *surface)
+{
+ if (surface != m_surface) {
+ qCWarning(qLcQpaInputMethods) << "Got leave event for surface without corresponding enter";
+ } else {
+ m_surface = nullptr;
+ }
+}
+
+QWaylandInputMethodContext::QWaylandInputMethodContext(QWaylandDisplay *display)
+ : m_display(display)
+{
+}
+
+QWaylandInputMethodContext::~QWaylandInputMethodContext()
+{
+}
+
+bool QWaylandInputMethodContext::isValid() const
+{
+ return m_display->textInputMethodManager() != nullptr;
+}
+
+void QWaylandInputMethodContext::reset()
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ inputMethod->reset();
+}
+
+void QWaylandInputMethodContext::commit()
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ inputMethod->commit();
+
+ m_display->forceRoundTrip();
+}
+
+void QWaylandInputMethodContext::update(Qt::InputMethodQueries queries)
+{
+ wl_surface *currentSurface = m_currentWindow != nullptr && m_currentWindow->handle() != nullptr
+ ? static_cast(m_currentWindow->handle())->wlSurface()
+ : nullptr;
+ if (currentSurface != nullptr && !inputMethodAccepted()) {
+ textInputMethod()->disable(currentSurface);
+ m_currentWindow.clear();
+ } else if (currentSurface == nullptr && inputMethodAccepted()) {
+ QWindow *window = QGuiApplication::focusWindow();
+ currentSurface = window != nullptr && window->handle() != nullptr
+ ? static_cast(window->handle())->wlSurface()
+ : nullptr;
+ if (currentSurface != nullptr) {
+ textInputMethod()->disable(currentSurface);
+ m_currentWindow = window;
+ }
+ }
+
+ queries &= (Qt::ImEnabled
+ | Qt::ImHints
+ | Qt::ImCursorRectangle
+ | Qt::ImCursorPosition
+ | Qt::ImSurroundingText
+ | Qt::ImCurrentSelection
+ | Qt::ImAnchorPosition
+ | Qt::ImTextAfterCursor
+ | Qt::ImTextBeforeCursor
+ | Qt::ImPreferredLanguage);
+
+ const Qt::InputMethodQueries queriesNeedingOffset = Qt::ImCursorPosition | Qt::ImSurroundingText | Qt::ImAnchorPosition;
+ if (queries & queriesNeedingOffset)
+ queries |= queriesNeedingOffset;
+
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr && QGuiApplication::focusObject() != nullptr) {
+ QInputMethodQueryEvent event(queries);
+ QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
+
+ inputMethod->start_update(int(queries));
+
+ if (queries & Qt::ImHints)
+ inputMethod->update_hints(event.value(Qt::ImHints).toInt());
+
+ if (queries & Qt::ImCursorRectangle) {
+ QRect rect = event.value(Qt::ImCursorRectangle).toRect();
+ inputMethod->update_cursor_rectangle(rect.x(), rect.y(), rect.width(), rect.height());
+ }
+
+ inputMethod->sendInputState(&event, queries);
+
+ if (queries & Qt::ImPreferredLanguage)
+ inputMethod->update_preferred_language(event.value(Qt::ImPreferredLanguage).toString());
+
+ inputMethod->end_update();
+
+ // ### Should we do a display sync here and ignore all events until it is received?
+ }
+}
+
+void QWaylandInputMethodContext::invokeAction(QInputMethod::Action action, int cursorPosition)
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ inputMethod->invoke_action(int(action), cursorPosition);
+}
+
+void QWaylandInputMethodContext::showInputPanel()
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ inputMethod->show_input_panel();
+}
+
+void QWaylandInputMethodContext::hideInputPanel()
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ inputMethod->hide_input_panel();
+}
+
+bool QWaylandInputMethodContext::isInputPanelVisible() const
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ return inputMethod->isVisible();
+ else
+ return false;
+}
+
+QRectF QWaylandInputMethodContext::keyboardRect() const
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ return inputMethod->keyboardRect();
+ else
+ return QRectF();
+}
+
+QLocale QWaylandInputMethodContext::locale() const
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ return inputMethod->locale();
+ else
+ return QLocale();
+}
+
+Qt::LayoutDirection QWaylandInputMethodContext::inputDirection() const
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod != nullptr)
+ return inputMethod->inputDirection();
+ else
+ return Qt::LeftToRight;
+}
+
+void QWaylandInputMethodContext::setFocusObject(QObject *)
+{
+ QWaylandTextInputMethod *inputMethod = textInputMethod();
+ if (inputMethod == nullptr)
+ return;
+
+ QWindow *window = QGuiApplication::focusWindow();
+
+ if (m_currentWindow != nullptr && m_currentWindow->handle() != nullptr) {
+ if (m_currentWindow.data() != window || !inputMethodAccepted()) {
+ auto *surface = static_cast(m_currentWindow->handle())->wlSurface();
+ if (surface)
+ inputMethod->disable(surface);
+ m_currentWindow.clear();
+ }
+ }
+
+ if (window != nullptr && window->handle() != nullptr && inputMethodAccepted()) {
+ if (m_currentWindow.data() != window) {
+ auto *surface = static_cast(window->handle())->wlSurface();
+ if (surface != nullptr) {
+ inputMethod->enable(surface);
+ m_currentWindow = window;
+ }
+ }
+ }
+}
+
+QWaylandTextInputMethod *QWaylandInputMethodContext::textInputMethod() const
+{
+ return m_display->defaultInputDevice()->textInputMethod();
+}
+
+} // QtWaylandClient
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wayland/qwaylandinputmethodcontext_p.h b/src/plugins/platforms/wayland/qwaylandinputmethodcontext_p.h
new file mode 100644
index 00000000000..8be65f447b3
--- /dev/null
+++ b/src/plugins/platforms/wayland/qwaylandinputmethodcontext_p.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 QWAYLANDINPUTMETHODCONTEXT_P_H
+#define QWAYLANDINPUTMETHODCONTEXT_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
+#include
+#include
+#include
+
+#include
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+ class QWaylandDisplay;
+
+class QWaylandTextInputMethod : public QtWayland::qt_text_input_method_v1
+{
+public:
+ QWaylandTextInputMethod(QWaylandDisplay *display, struct ::qt_text_input_method_v1 *textInputMethod);
+ ~QWaylandTextInputMethod() override;
+
+ void text_input_method_v1_visible_changed(int32_t visible) override;
+ void text_input_method_v1_enter(struct ::wl_surface *surface) override;
+ void text_input_method_v1_leave(struct ::wl_surface *surface) override;
+ void text_input_method_v1_locale_changed(const QString &localeName) override;
+ void text_input_method_v1_input_direction_changed(int32_t inputDirection) override;
+ void text_input_method_v1_keyboard_rectangle_changed(wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override;
+ void text_input_method_v1_key(int32_t type, int32_t key, int32_t modifiers, int32_t autoRepeat, int32_t count, int32_t nativeScanCode, int32_t nativeVirtualKey, int32_t nativeModifiers, const QString &text) override;
+ void text_input_method_v1_start_input_method_event(uint32_t serial, int32_t surrounding_text_offset) override;
+ void text_input_method_v1_end_input_method_event(uint32_t serial, const QString &commitString, const QString &preeditString, int32_t replacementStart, int32_t replacementLength) override;
+ void text_input_method_v1_input_method_event_attribute(uint32_t serial, int32_t type, int32_t start, int32_t length, const QString &value) override;
+
+ inline bool isVisible() const
+ {
+ return m_isVisible;
+ }
+
+ inline QRectF keyboardRect() const
+ {
+ return m_keyboardRect;
+ }
+
+ inline QLocale locale() const
+ {
+ return m_locale;
+ }
+
+ inline Qt::LayoutDirection inputDirection() const
+ {
+ return m_layoutDirection;
+ }
+
+ void sendInputState(QInputMethodQueryEvent *state, Qt::InputMethodQueries queries = Qt::ImQueryInput);
+
+private:
+ QWaylandDisplay *m_display;
+
+ QHash > m_pendingInputMethodEvents;
+ QHash m_offsetFromCompositor;
+
+ struct ::wl_surface *m_surface;
+
+ // Cached state
+ bool m_isVisible = false;
+ QRectF m_keyboardRect;
+ QLocale m_locale;
+ Qt::LayoutDirection m_layoutDirection;
+};
+
+class QWaylandInputMethodContext : public QPlatformInputContext
+{
+public:
+ QWaylandInputMethodContext(QWaylandDisplay *display);
+ ~QWaylandInputMethodContext() override;
+
+ bool isValid() const override;
+ void reset() override;
+ void commit() override;
+ void update(Qt::InputMethodQueries) override;
+ void invokeAction(QInputMethod::Action, int cursorPosition) override;
+ void showInputPanel() override;
+ void hideInputPanel() override;
+
+ bool isInputPanelVisible() const override;
+ QRectF keyboardRect() const override;
+ QLocale locale() const override;
+ Qt::LayoutDirection inputDirection() const override;
+
+ void setFocusObject(QObject *object) override;
+
+private:
+ QWaylandTextInputMethod *textInputMethod() const;
+
+ QWaylandDisplay *m_display;
+ QPointer m_currentWindow;
+};
+
+} // QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDINPUTMETHODCONTEXT_P_H
diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp
index 0aa18f09825..764acc2d919 100644
--- a/src/plugins/platforms/wayland/qwaylandintegration.cpp
+++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp
@@ -43,6 +43,7 @@
#include "qwaylandshmwindow_p.h"
#include "qwaylandinputdevice_p.h"
#include "qwaylandinputcontext_p.h"
+#include "qwaylandinputmethodcontext_p.h"
#include "qwaylandshmbackingstore_p.h"
#include "qwaylandnativeinterface_p.h"
#if QT_CONFIG(clipboard)
@@ -465,10 +466,14 @@ void QWaylandIntegration::reconfigureInputContext()
qCWarning(lcQpaWayland) << "qtvirtualkeyboard currently is not supported at client-side,"
" use QT_IM_MODULE=qtvirtualkeyboard at compositor-side.";
- if (requested.isNull())
- mInputContext.reset(new QWaylandInputContext(mDisplay.data()));
- else
+ if (requested.isNull()) {
+ if (mDisplay->textInputMethodManager() != nullptr)
+ mInputContext.reset(new QWaylandInputMethodContext(mDisplay.data()));
+ else
+ mInputContext.reset(new QWaylandInputContext(mDisplay.data()));
+ } else {
mInputContext.reset(QPlatformInputContextFactory::create(requested));
+ }
const QString defaultInputContext(QStringLiteral("compose"));
if ((!mInputContext || !mInputContext->isValid()) && requested != defaultInputContext)