diff options
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/client.pro | 4 | ||||
-rw-r--r-- | src/client/qwaylanddisplay.cpp | 10 | ||||
-rw-r--r-- | src/client/qwaylanddisplay_p.h | 6 | ||||
-rw-r--r-- | src/client/qwaylandinputcontext.cpp | 506 | ||||
-rw-r--r-- | src/client/qwaylandinputcontext_p.h | 77 | ||||
-rw-r--r-- | src/client/qwaylandinputdevice.cpp | 27 | ||||
-rw-r--r-- | src/client/qwaylandinputdevice_p.h | 6 |
7 files changed, 476 insertions, 160 deletions
diff --git a/src/client/client.pro b/src/client/client.pro index 1219ee34..61404eeb 100644 --- a/src/client/client.pro +++ b/src/client/client.pro @@ -39,7 +39,7 @@ WAYLANDCLIENTSOURCES += \ ../extensions/touch-extension.xml \ ../extensions/qtkey-extension.xml \ ../extensions/windowmanager.xml \ - ../3rdparty/protocol/text.xml \ + ../3rdparty/protocol/text-input-unstable-v2.xml \ ../3rdparty/protocol/xdg-shell.xml \ SOURCES += qwaylandintegration.cpp \ @@ -66,6 +66,7 @@ SOURCES += qwaylandintegration.cpp \ qwaylandqtkey.cpp \ ../shared/qwaylandmimehelper.cpp \ ../shared/qwaylandxkb.cpp \ + ../shared/qwaylandinputmethodeventbuilder.cpp \ qwaylandabstractdecoration.cpp \ qwaylanddecorationfactory.cpp \ qwaylanddecorationplugin.cpp \ @@ -100,6 +101,7 @@ HEADERS += qwaylandintegration_p.h \ qwaylandqtkey_p.h \ ../shared/qwaylandmimehelper.h \ ../shared/qwaylandxkb.h \ + ../shared/qwaylandinputmethodeventbuilder.h \ qwaylandabstractdecoration_p.h \ qwaylanddecorationfactory_p.h \ qwaylanddecorationplugin_p.h \ diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index 7244363c..a18b9853 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -50,6 +50,7 @@ #include "qwaylandxdgshell_p.h" #include "qwaylandxdgsurface_p.h" #include "qwaylandwlshellsurface_p.h" +#include "qwaylandinputcontext_p.h" #include "qwaylandwindowmanagerintegration_p.h" #include "qwaylandshellintegration_p.h" @@ -60,7 +61,7 @@ #include "qwaylandtouch_p.h" #include "qwaylandqtkey_p.h" -#include <QtWaylandClient/private/qwayland-text.h> +#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> #include <QtWaylandClient/private/qwayland-xdg-shell.h> #include <QtCore/QAbstractEventDispatcher> @@ -281,8 +282,11 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mTouchExtension.reset(new QWaylandTouchExtension(this, id)); } else if (interface == QStringLiteral("qt_key_extension")) { mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); - } else if (interface == QStringLiteral("wl_text_input_manager")) { - mTextInputManager.reset(new QtWayland::wl_text_input_manager(registry, id, 1)); + } else if (interface == QStringLiteral("zwp_text_input_manager_v2")) { + mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1)); + foreach (QWaylandInputDevice *inputDevice, mInputDevices) { + inputDevice->setTextInput(new QWaylandTextInput(this, mTextInputManager->get_text_input(inputDevice->wl_seat()))); + } } else if (interface == QStringLiteral("qt_hardware_integration")) { mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); // make a roundtrip here since we need to receive the events sent by diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index 82d87cb9..618e57c5 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -76,7 +76,7 @@ namespace QtWayland { class qt_shell; class qt_sub_surface_extension; class qt_surface_extension; - class wl_text_input_manager; + class zwp_text_input_manager_v2; class xdg_shell; } @@ -147,7 +147,7 @@ public: QtWayland::qt_surface_extension *windowExtension() const { return mWindowExtension.data(); } QWaylandTouchExtension *touchExtension() const { return mTouchExtension.data(); } - QtWayland::wl_text_input_manager *textInputManager() const { return mTextInputManager.data(); } + QtWayland::zwp_text_input_manager_v2 *textInputManager() const { return mTextInputManager.data(); } QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); } struct RegistryGlobal { @@ -207,7 +207,7 @@ private: QScopedPointer<QWaylandTouchExtension> mTouchExtension; QScopedPointer<QWaylandQtKeyExtension> mQtKeyExtension; QScopedPointer<QWaylandWindowManagerIntegration> mWindowManagerIntegration; - QScopedPointer<QtWayland::wl_text_input_manager> mTextInputManager; + QScopedPointer<QtWayland::zwp_text_input_manager_v2> mTextInputManager; QScopedPointer<QWaylandHardwareIntegration> mHardwareIntegration; QSocketNotifier *mReadNotifier; int mFd; diff --git a/src/client/qwaylandinputcontext.cpp b/src/client/qwaylandinputcontext.cpp index 00f6a430..aeaf415d 100644 --- a/src/client/qwaylandinputcontext.cpp +++ b/src/client/qwaylandinputcontext.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWaylandClient module of the Qt Toolkit. @@ -40,147 +40,358 @@ #include "qwaylandinputcontext_p.h" -#include <QGuiApplication> -#include <QWindow> -#ifndef QT_NO_WAYLAND_XKB -#include <xkbcommon/xkbcommon.h> -#endif +#include <QtGui/QGuiApplication> +#include <QtGui/QTextCharFormat> +#include <QtGui/QWindow> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformintegration.h> #include "qwaylanddisplay_p.h" #include "qwaylandinputdevice_p.h" +#include "qwaylandinputmethodeventbuilder.h" #include "qwaylandwindow_p.h" +#include "qwaylandxkb.h" QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(qLcQpaInputMethods, "qt.qpa.input.methods") + namespace QtWaylandClient { -static Qt::Key toQtKey(uint32_t sym) -{ -#ifndef QT_NO_WAYLAND_XKB - switch (static_cast<xkb_keysym_t>(sym)) { - case XKB_KEY_BackSpace: - return Qt::Key_Backspace; - case XKB_KEY_Return: - return Qt::Key_Return; - case XKB_KEY_Left: - return Qt::Key_Left; - case XKB_KEY_Up: - return Qt::Key_Up; - case XKB_KEY_Right: - return Qt::Key_Right; - case XKB_KEY_Down: - return Qt::Key_Down; - default: - return Qt::Key_unknown; - } -#else - Q_UNUSED(sym) - return Qt::Key_unknown; -#endif +namespace { +const Qt::InputMethodQueries supportedQueries = Qt::ImEnabled | + Qt::ImSurroundingText | + Qt::ImCursorPosition | + Qt::ImAnchorPosition | + Qt::ImHints | + Qt::ImCursorRectangle | + Qt::ImPreferredLanguage; } -static QEvent::Type toQEventType(uint32_t state) +QWaylandTextInput::QWaylandTextInput(QWaylandDisplay *display, struct ::zwp_text_input_v2 *text_input) + : QtWayland::zwp_text_input_v2(text_input) + , m_display(display) + , m_builder() + , m_serial(0) + , m_surface(nullptr) + , m_preeditCommit() + , m_inputPanelVisible(false) + , m_keyboardRectangle() + , m_locale() + , m_inputDirection(Qt::LayoutDirectionAuto) + , m_resetCallback(nullptr) { - switch (static_cast<wl_keyboard_key_state>(state)) { - default: - case WL_KEYBOARD_KEY_STATE_PRESSED: - return QEvent::KeyPress; - case WL_KEYBOARD_KEY_STATE_RELEASED: - return QEvent::KeyRelease; - } } -QWaylandTextInput::QWaylandTextInput(struct ::wl_text_input *text_input) - : QtWayland::wl_text_input(text_input) - , m_commit() - , m_serial(0) - , m_resetSerial(0) +QWaylandTextInput::~QWaylandTextInput() { + if (m_resetCallback) + wl_callback_destroy(m_resetCallback); } -QString QWaylandTextInput::commitString() const +void QWaylandTextInput::reset() { - return m_commit; + m_builder.reset(); + m_preeditCommit = QString(); + updateState(Qt::ImQueryAll, update_state_reset); } -void QWaylandTextInput::reset() +void QWaylandTextInput::commit() { - wl_text_input::reset(); - updateState(); - m_resetSerial = m_serial; + if (QObject *o = QGuiApplication::focusObject()) { + QInputMethodEvent event; + event.setCommitString(m_preeditCommit); + QCoreApplication::sendEvent(o, &event); + } + + reset(); } -void QWaylandTextInput::updateState() +const wl_callback_listener QWaylandTextInput::callbackListener = { + QWaylandTextInput::resetCallback +}; + +void QWaylandTextInput::resetCallback(void *data, wl_callback *, uint32_t) +{ + QWaylandTextInput *self = static_cast<QWaylandTextInput*>(data); + + if (self->m_resetCallback) { + wl_callback_destroy(self->m_resetCallback); + self->m_resetCallback = nullptr; + } +} + +void QWaylandTextInput::updateState(Qt::InputMethodQueries queries, uint32_t flags) { if (!QGuiApplication::focusObject()) return; - QInputMethodQueryEvent event(Qt::ImQueryAll); + if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle()) + return; + + struct ::wl_surface *surface = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle())->object(); + if (!surface || (surface != m_surface)) + return; + + queries &= supportedQueries; + + // Surrounding text, cursor and anchor positions are transferred together + if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) + queries |= Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition; + + QInputMethodQueryEvent event(queries); QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); - const QString &text = event.value(Qt::ImSurroundingText).toString(); - const int cursor = event.value(Qt::ImCursorPosition).toInt(); - const int anchor = event.value(Qt::ImAnchorPosition).toInt(); + 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(); + + // Make sure text is not too big + if (text.toUtf8().size() > 2048) { + int c = qAbs(cursor - anchor) <= 512 ? qMin(cursor, anchor) + qAbs(cursor - anchor) / 2: cursor; + + const int offset = c - qBound(0, c, 512 - qMin(text.size() - c, 256)); + text = text.mid(offset + c - 256, 512); + cursor -= offset; + anchor -= offset; + } + + set_surrounding_text(text, text.leftRef(cursor).toUtf8().size(), text.leftRef(anchor).toUtf8().size()); + } + + if (queries & Qt::ImHints) { + QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convert(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt())); + set_content_type(contentType.hint, contentType.purpose); + } + + if (queries & Qt::ImCursorRectangle) { + const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect(); + const QRect &tRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect); + set_cursor_rectangle(tRect.x(), tRect.y(), tRect.width(), tRect.height()); + } + + if (queries & Qt::ImPreferredLanguage) { + const QString &language = event.value(Qt::ImPreferredLanguage).toString(); + set_preferred_language(language); + } + + update_state(m_serial, flags); + if (flags != update_state_change) { + if (m_resetCallback) + wl_callback_destroy(m_resetCallback); + m_resetCallback = wl_display_sync(m_display->wl_display()); + wl_callback_add_listener(m_resetCallback, &QWaylandTextInput::callbackListener, this); + } +} + +bool QWaylandTextInput::isInputPanelVisible() const +{ + return m_inputPanelVisible; +} + +QRectF QWaylandTextInput::keyboardRect() const +{ + return m_keyboardRectangle; +} + +QLocale QWaylandTextInput::locale() const +{ + return m_locale; +} + +Qt::LayoutDirection QWaylandTextInput::inputDirection() const +{ + return m_inputDirection; +} - set_surrounding_text(text, text.leftRef(cursor).toUtf8().size(), text.leftRef(anchor).toUtf8().size()); +void QWaylandTextInput::zwp_text_input_v2_enter(uint32_t serial, ::wl_surface *surface) +{ + m_serial = serial; + m_surface = surface; - commit_state(++m_serial); + updateState(Qt::ImQueryAll, update_state_enter); } -void QWaylandTextInput::text_input_preedit_string(uint32_t serial, const QString &text, const QString &commit) +void QWaylandTextInput::zwp_text_input_v2_leave(uint32_t serial, ::wl_surface *surface) { - Q_UNUSED(serial) + m_serial = serial; + + if (m_surface != surface) { + qCDebug(qLcQpaInputMethods()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "focused surface" << m_surface; + } + + m_surface = nullptr; +} + +void QWaylandTextInput::zwp_text_input_v2_modifiers_map(wl_array *map) +{ + QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0'); + + m_modifiersMap.clear(); + + Q_FOREACH (const QByteArray &modifier, modifiersMap) { + if (modifier == "Shift") + m_modifiersMap.append(Qt::ShiftModifier); + else if (modifier == "Control") + m_modifiersMap.append(Qt::ControlModifier); + else if (modifier == "Alt") + m_modifiersMap.append(Qt::AltModifier); + else if (modifier == "Mod1") + m_modifiersMap.append(Qt::AltModifier); + else if (modifier == "Mod4") + m_modifiersMap.append(Qt::MetaModifier); + else + m_modifiersMap.append(Qt::NoModifier); + } +} + +void QWaylandTextInput::zwp_text_input_v2_input_panel_state(uint32_t visible, int32_t x, int32_t y, int32_t width, int32_t height) +{ + const bool inputPanelVisible = (visible == input_panel_visibility_visible); + if (m_inputPanelVisible != inputPanelVisible) { + m_inputPanelVisible = inputPanelVisible; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputPanelVisibleChanged(); + } + const QRectF keyboardRectangle(x, y, width, height); + if (m_keyboardRectangle != keyboardRectangle) { + m_keyboardRectangle = keyboardRectangle; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitKeyboardRectChanged(); + } +} + +void QWaylandTextInput::zwp_text_input_v2_preedit_string(const QString &text, const QString &commit) +{ + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard preedit_string: reset not confirmed"; + m_builder.reset(); + return; + } + if (!QGuiApplication::focusObject()) return; - m_commit = commit; - QList<QInputMethodEvent::Attribute> attributes; - QInputMethodEvent event(text, attributes); + QInputMethodEvent event = m_builder.buildPreedit(text); + + m_builder.reset(); + m_preeditCommit = commit; + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); } -void QWaylandTextInput::text_input_commit_string(uint32_t serial, const QString &text) +void QWaylandTextInput::zwp_text_input_v2_preedit_styling(uint32_t index, uint32_t length, uint32_t style) +{ + m_builder.addPreeditStyling(index, length, style); +} + +void QWaylandTextInput::zwp_text_input_v2_preedit_cursor(int32_t index) { - Q_UNUSED(serial); + m_builder.setPreeditCursor(index); +} + +void QWaylandTextInput::zwp_text_input_v2_commit_string(const QString &text) +{ + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard commit_string: reset not confirmed"; + m_builder.reset(); + return; + } + if (!QGuiApplication::focusObject()) return; - QInputMethodEvent event; - event.setCommitString(text); + QInputMethodEvent event = m_builder.buildCommit(text); + + m_builder.reset(); + QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); +} - m_commit = QString(); +void QWaylandTextInput::zwp_text_input_v2_cursor_position(int32_t index, int32_t anchor) +{ + m_builder.setCursorPosition(index, anchor); } -void QWaylandTextInput::text_input_enter(wl_surface *) +void QWaylandTextInput::zwp_text_input_v2_delete_surrounding_text(uint32_t before_length, uint32_t after_length) { - updateState(); - m_resetSerial = m_serial; + m_builder.setDeleteSurroundingText(before_length, after_length); } -void QWaylandTextInput::text_input_leave() +void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) { - if (!m_commit.isEmpty()) - text_input_commit_string(0, m_commit); + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed"; + return; + } + + if (!QGuiApplication::focusWindow()) + return; + + Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers); + + QEvent::Type type = QWaylandXkb::toQtEventType(state); + const QString &text = QWaylandXkb::textFromKeysym(sym, qtModifiers); + int qtkey = QWaylandXkb::keysymToQtKey(sym, qtModifiers, text); + + QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(), + time, type, qtkey, qtModifiers, text); } -void QWaylandTextInput::text_input_keysym(uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) +void QWaylandTextInput::zwp_text_input_v2_language(const QString &language) { - Q_UNUSED(serial); - Q_UNUSED(time); - Q_UNUSED(modifiers); - if (!QGuiApplication::focusObject()) + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard language: reset not confirmed"; return; + } - // TODO: Convert modifiers to Qt::KeyboardModifiers. - QKeyEvent event(toQEventType(state), toQtKey(sym), Qt::NoModifier); - QCoreApplication::sendEvent(qGuiApp->focusWindow(), &event); + const QLocale locale(language); + if (m_locale != locale) { + m_locale = locale; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitLocaleChanged(); + } +} + +void QWaylandTextInput::zwp_text_input_v2_text_direction(uint32_t direction) +{ + if (m_resetCallback) { + qCDebug(qLcQpaInputMethods()) << "discard text_direction: reset not confirmed"; + return; + } + + const Qt::LayoutDirection inputDirection = (direction == text_direction_auto) ? Qt::LayoutDirectionAuto : + (direction == text_direction_ltr) ? Qt::LeftToRight : + (direction == text_direction_rtl) ? Qt::RightToLeft : Qt::LayoutDirectionAuto; + if (m_inputDirection != inputDirection) { + m_inputDirection = inputDirection; + QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputDirectionChanged(m_inputDirection); + } +} + +void QWaylandTextInput::zwp_text_input_v2_input_method_changed(uint32_t serial, uint32_t flags) +{ + Q_UNUSED(flags); + + m_serial = serial; + updateState(Qt::ImQueryAll, update_state_full); +} + +Qt::KeyboardModifiers QWaylandTextInput::modifiersToQtModifiers(uint32_t modifiers) +{ + Qt::KeyboardModifiers ret = Qt::NoModifier; + for (int i = 0; modifiers >>= 1; ++i) { + ret |= m_modifiersMap[i]; + } + return ret; } QWaylandInputContext::QWaylandInputContext(QWaylandDisplay *display) : QPlatformInputContext() , mDisplay(display) - , mTextInput() + , mCurrentWindow() +{ +} + +QWaylandInputContext::~QWaylandInputContext() { } @@ -191,96 +402,141 @@ bool QWaylandInputContext::isValid() const void QWaylandInputContext::reset() { - if (!ensureTextInput()) + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + + QPlatformInputContext::reset(); + + if (!textInput()) return; - mTextInput->reset(); + textInput()->reset(); } void QWaylandInputContext::commit() { - if (!ensureTextInput()) - return; + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!QGuiApplication::focusObject()) + if (!textInput()) return; - QInputMethodEvent event; - event.setCommitString(mTextInput->commitString()); - QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event); - - mTextInput->reset(); + textInput()->commit(); } void QWaylandInputContext::update(Qt::InputMethodQueries queries) { - Q_UNUSED(queries); - if (!ensureTextInput()) - return; - - mTextInput->updateState(); -} + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO << queries; -void QWaylandInputContext::invokeAction(QInputMethod::Action, int cursorPosition) -{ - if (!ensureTextInput()) + if (!QGuiApplication::focusObject() || !textInput()) return; - mTextInput->invoke_action(0, cursorPosition); // FIXME button, to UTF8 cursor position + if (mCurrentWindow && mCurrentWindow->handle() && !inputMethodAccepted()) { + struct ::wl_surface *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->object(); + textInput()->disable(surface); + mCurrentWindow.clear(); + } else if (!mCurrentWindow && inputMethodAccepted()) { + QWindow *window = QGuiApplication::focusWindow(); + if (window && window->handle()) { + struct ::wl_surface *surface = static_cast<QWaylandWindow *>(window->handle())->object(); + textInput()->enable(surface); + mCurrentWindow = window; + } + } + + textInput()->updateState(queries, QtWayland::zwp_text_input_v2::update_state_change); } void QWaylandInputContext::showInputPanel() { - if (!ensureTextInput()) + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + + if (!textInput()) return; - mTextInput->show_input_panel(); + textInput()->show_input_panel(); } void QWaylandInputContext::hideInputPanel() { - if (!ensureTextInput()) + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + + if (!textInput()) return; - mTextInput->hide_input_panel(); + textInput()->hide_input_panel(); } bool QWaylandInputContext::isInputPanelVisible() const { - return false; + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + + if (!textInput()) + return QPlatformInputContext::isInputPanelVisible(); + + return textInput()->isInputPanelVisible(); } -void QWaylandInputContext::setFocusObject(QObject *object) +QRectF QWaylandInputContext::keyboardRect() const { - if (!ensureTextInput()) - return; + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; - if (!object) { - mTextInput->deactivate(mDisplay->defaultInputDevice()->wl_seat()); - return; - } + if (!textInput()) + return QPlatformInputContext::keyboardRect(); - QWindow *window = QGuiApplication::focusWindow(); - if (!window || !window->handle()) - return; + return textInput()->keyboardRect(); +} - struct ::wl_surface *surface = static_cast<QWaylandWindow *>(window->handle())->object(); - mTextInput->activate(mDisplay->defaultInputDevice()->wl_seat(), surface); +QLocale QWaylandInputContext::locale() const +{ + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + + if (!textInput()) + return QPlatformInputContext::locale(); + + return textInput()->locale(); } -bool QWaylandInputContext::ensureTextInput() +Qt::LayoutDirection QWaylandInputContext::inputDirection() const { - if (mTextInput) - return true; + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + + if (!textInput()) + return QPlatformInputContext::inputDirection(); + + return textInput()->inputDirection(); +} + +void QWaylandInputContext::setFocusObject(QObject *) +{ + qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO; + + if (!textInput()) + return; - if (!isValid()) - return false; + QWindow *window = QGuiApplication::focusWindow(); - mTextInput.reset(new QWaylandTextInput(mDisplay->textInputManager()->create_text_input())); - return true; + if (mCurrentWindow && mCurrentWindow->handle()) { + if (mCurrentWindow.data() != window || !inputMethodAccepted()) { + struct ::wl_surface *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->object(); + textInput()->disable(surface); + mCurrentWindow.clear(); + } + } + + if (window && window->handle() && inputMethodAccepted()) { + if (mCurrentWindow.data() != window) { + struct ::wl_surface *surface = static_cast<QWaylandWindow *>(window->handle())->object(); + textInput()->enable(surface); + mCurrentWindow = window; + } + textInput()->updateState(Qt::ImQueryAll, QtWayland::zwp_text_input_v2::update_state_enter); + } } +QWaylandTextInput *QWaylandInputContext::textInput() const +{ + return mDisplay->defaultInputDevice()->textInput(); } -QT_END_NAMESPACE +} +QT_END_NAMESPACE diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h index cdabfcca..0429dd14 100644 --- a/src/client/qwaylandinputcontext_p.h +++ b/src/client/qwaylandinputcontext_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWaylandClient module of the Qt Toolkit. @@ -54,36 +54,75 @@ #include <qpa/qplatforminputcontext.h> -#include <QtWaylandClient/private/qwayland-text.h> +#include <QLoggingCategory> +#include <QPointer> +#include <QRectF> +#include <QVector> + +#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> +#include <qwaylandinputmethodeventbuilder.h> QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods) + namespace QtWaylandClient { class QWaylandDisplay; -class QWaylandTextInput : public QtWayland::wl_text_input +class QWaylandTextInput : public QtWayland::zwp_text_input_v2 { public: - QWaylandTextInput(struct ::wl_text_input *text_input); - - QString commitString() const; + QWaylandTextInput(QWaylandDisplay *display, struct ::zwp_text_input_v2 *text_input); + ~QWaylandTextInput(); void reset(); - void updateState(); + void commit(); + void updateState(Qt::InputMethodQueries queries, uint32_t flags); + + bool isInputPanelVisible() const; + QRectF keyboardRect() const; + + QLocale locale() const; + Qt::LayoutDirection inputDirection() const; protected: - void text_input_preedit_string(uint32_t serial, const QString &text, const QString &commit) Q_DECL_OVERRIDE; - void text_input_commit_string(uint32_t serial, const QString &text) Q_DECL_OVERRIDE; - void text_input_enter(wl_surface *surface) Q_DECL_OVERRIDE; - void text_input_leave() Q_DECL_OVERRIDE; - void text_input_keysym(uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers); + void zwp_text_input_v2_enter(uint32_t serial, struct ::wl_surface *surface) Q_DECL_OVERRIDE; + void zwp_text_input_v2_leave(uint32_t serial, struct ::wl_surface *surface) Q_DECL_OVERRIDE; + void zwp_text_input_v2_modifiers_map(wl_array *map) Q_DECL_OVERRIDE; + void zwp_text_input_v2_input_panel_state(uint32_t state, int32_t x, int32_t y, int32_t width, int32_t height) Q_DECL_OVERRIDE; + void zwp_text_input_v2_preedit_string(const QString &text, const QString &commit) Q_DECL_OVERRIDE; + void zwp_text_input_v2_preedit_styling(uint32_t index, uint32_t length, uint32_t style) Q_DECL_OVERRIDE; + void zwp_text_input_v2_preedit_cursor(int32_t index) Q_DECL_OVERRIDE; + void zwp_text_input_v2_commit_string(const QString &text) Q_DECL_OVERRIDE; + void zwp_text_input_v2_cursor_position(int32_t index, int32_t anchor) Q_DECL_OVERRIDE; + void zwp_text_input_v2_delete_surrounding_text(uint32_t before_length, uint32_t after_length) Q_DECL_OVERRIDE; + void zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) Q_DECL_OVERRIDE; + void zwp_text_input_v2_language(const QString &language) Q_DECL_OVERRIDE; + void zwp_text_input_v2_text_direction(uint32_t direction) Q_DECL_OVERRIDE; + void zwp_text_input_v2_input_method_changed(uint32_t serial, uint32_t flags) Q_DECL_OVERRIDE; private: - QString m_commit; + Qt::KeyboardModifiers modifiersToQtModifiers(uint32_t modifiers); + + QWaylandDisplay *m_display; + QWaylandInputMethodEventBuilder m_builder; + + QVector<Qt::KeyboardModifier> m_modifiersMap; uint32_t m_serial; - uint32_t m_resetSerial; + struct ::wl_surface *m_surface; + + QString m_preeditCommit; + + bool m_inputPanelVisible; + QRectF m_keyboardRectangle; + QLocale m_locale; + Qt::LayoutDirection m_inputDirection; + + struct ::wl_callback *m_resetCallback; + static const wl_callback_listener callbackListener; + static void resetCallback(void *data, struct wl_callback *wl_callback, uint32_t time); }; class QWaylandInputContext : public QPlatformInputContext @@ -91,25 +130,29 @@ class QWaylandInputContext : public QPlatformInputContext Q_OBJECT public: explicit QWaylandInputContext(QWaylandDisplay *display); + ~QWaylandInputContext(); bool isValid() const Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; void commit() Q_DECL_OVERRIDE; void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE; - void invokeAction(QInputMethod::Action, int cursorPosition) Q_DECL_OVERRIDE; void showInputPanel() Q_DECL_OVERRIDE; void hideInputPanel() Q_DECL_OVERRIDE; bool isInputPanelVisible() const Q_DECL_OVERRIDE; + QRectF keyboardRect() const Q_DECL_OVERRIDE; + + QLocale locale() const Q_DECL_OVERRIDE; + Qt::LayoutDirection inputDirection() const Q_DECL_OVERRIDE; void setFocusObject(QObject *object) Q_DECL_OVERRIDE; private: - bool ensureTextInput(); + QWaylandTextInput *textInput() const; QWaylandDisplay *mDisplay; - QScopedPointer<QWaylandTextInput> mTextInput; + QPointer<QWindow> mCurrentWindow; }; } diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index 46c473e7..cf1c7ac4 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -50,6 +50,7 @@ #include "qwaylanddisplay_p.h" #include "qwaylandshmbackingstore_p.h" #include "../shared/qwaylandxkb.h" +#include "qwaylandinputcontext_p.h" #include <QtGui/private/qpixmap_raster_p.h> #include <QtGui/private/qguiapplication_p.h> @@ -185,6 +186,7 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, , mKeyboard(0) , mPointer(0) , mTouch(0) + , mTextInput(0) , mTime(0) , mSerial(0) , mTouchDevice(0) @@ -193,6 +195,9 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, mDataDevice = mQDisplay->dndSelectionHandler()->getDataDevice(this); } + if (mQDisplay->textInputManager()) { + mTextInput = new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())); + } } QWaylandInputDevice::~QWaylandInputDevice() @@ -277,6 +282,16 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const return mDataDevice; } +void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput) +{ + mTextInput = textInput; +} + +QWaylandTextInput *QWaylandInputDevice::textInput() const +{ + return mTextInput; +} + void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button) { if (mPointer) @@ -693,19 +708,9 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, Qt::KeyboardModifiers modifiers = mParent->modifiers(); - uint utf32 = xkb_keysym_to_utf32(sym); - if (utf32) - text = QString::fromUcs4(&utf32, 1); - + text = QWaylandXkb::textFromKeysym(sym, modifiers); qtkey = QWaylandXkb::keysymToQtKey(sym, modifiers, text); - - // Map control + letter to proper text - if (utf32 >= 'A' && utf32 <= '~' && (modifiers & Qt::ControlModifier)) { - utf32 &= ~0x60; - text = QString::fromUcs4(&utf32, 1); - } - sendKey(window->window(), time, type, qtkey, modifiers, code, sym, mNativeModifiers, text); #else // Generic fallback for single hard keys: Assume 'key' is a Qt key code. diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index 0da45c38..82b9d90c 100644 --- a/src/client/qwaylandinputdevice_p.h +++ b/src/client/qwaylandinputdevice_p.h @@ -80,6 +80,7 @@ namespace QtWaylandClient { class QWaylandWindow; class QWaylandDisplay; class QWaylandDataDevice; +class QWaylandTextInput; class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice : public QObject @@ -108,6 +109,9 @@ public: void setDataDevice(QWaylandDataDevice *device); QWaylandDataDevice *dataDevice() const; + void setTextInput(QWaylandTextInput *textInput); + QWaylandTextInput *textInput() const; + void removeMouseButtonFromState(Qt::MouseButton button); QWaylandWindow *pointerFocus() const; @@ -138,6 +142,8 @@ private: Pointer *mPointer; Touch *mTouch; + QWaylandTextInput *mTextInput; + uint32_t mTime; uint32_t mSerial; |