From c57da9bb0a2d475b72024997657e250d171dcbaa Mon Sep 17 00:00:00 2001 From: Inho Lee Date: Fri, 17 Jan 2025 11:02:27 +0100 Subject: [PATCH] textinputv3: implement enableSurface() and disableSurface() When enter() happens, it got enabled unconditionally. Then in QWaylandInputContext::setFocusObject(), we need to enable() or disable() it based on real situation.(If previous enter is not valid on the QObject, it will be disabled by disableSurface().) It fixes the issue for wayfire and kwin/plasma 6. Sway still has issues with input method events with popup menu, with and without this patch.(Sway doesn't make keyboard_leave/enter after surface_enter of popups.) See also https://github.com/swaywm/sway/issues/4406 Fixes: QTBUG-132196 Pick-to: 6.9 6.8 Done-with: Liang Qi Change-Id: I5b29a86e0256868c8bcbe48f936c09ee013728a6 Reviewed-by: Liang Qi --- .../wayland/qwaylandinputcontext.cpp | 35 +++++----- .../platforms/wayland/qwaylandtextinputv3.cpp | 65 +++++++++++++++---- .../platforms/wayland/qwaylandtextinputv3_p.h | 6 +- 3 files changed, 74 insertions(+), 32 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandinputcontext.cpp b/src/plugins/platforms/wayland/qwaylandinputcontext.cpp index 4d70f35facc..0ccc4dba57a 100644 --- a/src/plugins/platforms/wayland/qwaylandinputcontext.cpp +++ b/src/plugins/platforms/wayland/qwaylandinputcontext.cpp @@ -189,25 +189,30 @@ void QWaylandInputContext::setFocusObject(QObject *object) QWindow *window = QGuiApplication::focusWindow(); - if (mCurrentWindow && mCurrentWindow->handle()) { - if (mCurrentWindow.data() != window || !inputMethodAccepted()) { - auto *surface = static_cast(mCurrentWindow->handle())->wlSurface(); - if (surface) - inputInterface->disableSurface(surface); - mCurrentWindow.clear(); - } - } - - if (window && window->handle() && inputMethodAccepted()) { + if (window && window->handle()) { if (mCurrentWindow.data() != window) { - auto *surface = static_cast(window->handle())->wlSurface(); - if (surface) { - inputInterface->enableSurface(surface); - mCurrentWindow = window; + if (!inputMethodAccepted()) { + auto *surface = static_cast(window->handle())->wlSurface(); + if (surface) + inputInterface->disableSurface(surface); + mCurrentWindow.clear(); + } else { + auto *surface = static_cast(window->handle())->wlSurface(); + if (surface) { + inputInterface->enableSurface(surface); + mCurrentWindow = window; + } else { + mCurrentWindow.clear(); + } } } - inputInterface->updateState(Qt::ImQueryAll, QWaylandTextInputInterface::update_state_enter); + if (mCurrentWindow) + inputInterface->updateState(Qt::ImQueryAll, QWaylandTextInputInterface::update_state_enter); + return; } + + if (mCurrentWindow) + mCurrentWindow.clear(); } QWaylandTextInputInterface *QWaylandInputContext::textInput() const diff --git a/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp b/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp index b35f05b2f16..a9538370781 100644 --- a/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp +++ b/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp @@ -40,12 +40,16 @@ const Qt::InputMethodQueries supportedQueries3 = Qt::ImEnabled | Qt::ImCursorRectangle; } -void QWaylandTextInputv3::zwp_text_input_v3_enter(struct ::wl_surface *surface) +void QWaylandTextInputv3::enableSurface(::wl_surface *surface) { - qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << m_surface << surface; + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << surface; + + if (m_surface == surface) + return; // already enabled + if (m_surface) + qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "Try to enable surface" << surface << "with focusing surface" << m_surface; m_surface = surface; - m_pendingPreeditString.clear(); m_pendingCommitString.clear(); m_pendingDeleteBeforeText = 0; @@ -55,27 +59,51 @@ void QWaylandTextInputv3::zwp_text_input_v3_enter(struct ::wl_surface *surface) updateState(supportedQueries3, update_state_enter); } +void QWaylandTextInputv3::disableSurface(::wl_surface *surface) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << surface; + + if (!m_surface) + return; // already disabled + if (m_surface != surface) + qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "Try to disable surface" << surface << "with focusing surface" << m_surface; + + m_currentPreeditString.clear(); + m_surface = nullptr; + disable(); + commit(); +} + +void QWaylandTextInputv3::zwp_text_input_v3_enter(struct ::wl_surface *surface) +{ + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << m_surface << surface; + + if (m_surface) + qCWarning(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Got enter event without leaving a surface " << m_surface; + + enableSurface(surface); +} + void QWaylandTextInputv3::zwp_text_input_v3_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; - } + if (!m_surface) + return; // Nothing to leave - m_currentPreeditString.clear(); + if (m_surface != surface) + qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "with focusing surface" << m_surface; - m_surface = nullptr; - - disable(); - commit(); - qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Done"; + disableSurface(surface); } void QWaylandTextInputv3::zwp_text_input_v3_preedit_string(const QString &text, int32_t cursorBegin, int32_t cursorEnd) { qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text << cursorBegin << cursorEnd; + if (!m_surface) { + qCWarning(qLcQpaWaylandTextInput) << "Got preedit_string event without entering a surface"; + return; + } if (!QGuiApplication::focusObject()) return; @@ -88,6 +116,10 @@ void QWaylandTextInputv3::zwp_text_input_v3_preedit_string(const QString &text, void QWaylandTextInputv3::zwp_text_input_v3_commit_string(const QString &text) { qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text; + if (!m_surface) { + qCWarning(qLcQpaWaylandTextInput) << "Got commit_string event without entering a surface"; + return; + } if (!QGuiApplication::focusObject()) return; @@ -98,6 +130,10 @@ void QWaylandTextInputv3::zwp_text_input_v3_commit_string(const QString &text) void QWaylandTextInputv3::zwp_text_input_v3_delete_surrounding_text(uint32_t beforeText, uint32_t afterText) { qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << beforeText << afterText; + if (!m_surface) { + qCWarning(qLcQpaWaylandTextInput) << "Got delete_surrounding_text event without entering a surface"; + return; + } if (!QGuiApplication::focusObject()) return; @@ -110,6 +146,9 @@ void QWaylandTextInputv3::zwp_text_input_v3_done(uint32_t serial) { qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << serial << m_currentSerial; + if (!m_surface) + return; + // This is a case of double click. // text_input_v3 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)) { diff --git a/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h b/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h index 4ea82b1f592..9a0d7e8a5f4 100644 --- a/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h +++ b/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h @@ -48,10 +48,8 @@ public: QLocale locale() const override; Qt::LayoutDirection inputDirection() const override; - // doing nothing in zwp_text_input_v3. - // enter() and leave() takes the role to enable/disable the surface - void enableSurface(::wl_surface *) override {}; - void disableSurface(::wl_surface *) override {}; + void enableSurface(::wl_surface *) override; + void disableSurface(::wl_surface *) override; protected: void zwp_text_input_v3_enter(struct ::wl_surface *surface) override;