From a74c4361564217919437986feb91a3f85c5a5c33 Mon Sep 17 00:00:00 2001 From: Weng Xuetian Date: Fri, 10 Jun 2022 16:23:52 -0700 Subject: [PATCH] Always send a done after commit for text input v3. In Gtk's text-input-v3 implementation it expect done to update the client serial after every commit. Though it is unclear whether this is a protocol requirements, do the same thing like mutter for more compatiblity, especially Gtk3 is in EOL not likely to be patched any time soon. To do so, we will need to keep track of the last active preedit, otherwise only send_done() will clear the preedit. --- .../server/test_textinputv3_interface.cpp | 6 ++-- src/wayland/textinput_v3_interface.cpp | 29 +++++++++++++++++++ src/wayland/textinput_v3_interface_p.h | 8 +++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/wayland/autotests/server/test_textinputv3_interface.cpp b/src/wayland/autotests/server/test_textinputv3_interface.cpp index efe09b5833..bb6bbf2ec8 100644 --- a/src/wayland/autotests/server/test_textinputv3_interface.cpp +++ b/src/wayland/autotests/server/test_textinputv3_interface.cpp @@ -292,6 +292,7 @@ void TestTextInputV3Interface::testEvents() QSignalSpy focusedSurfaceChangedSpy(m_seat, &SeatInterface::focusedTextInputSurfaceChanged); QSignalSpy textInputEnabledSpy(m_serverTextInputV3, &TextInputV3Interface::enabledChanged); + QSignalSpy doneSpy(m_clientTextInputV3, &TextInputV3::done); // Enter the textinput QVERIFY(focusedSurfaceChangedSpy.isValid()); @@ -308,11 +309,12 @@ void TestTextInputV3Interface::testEvents() m_clientTextInputV3->commit(); m_totalCommits++; QVERIFY(textInputEnabledSpy.wait()); + QVERIFY(doneSpy.wait()); + QCOMPARE(doneSpy.count(), 1); QSignalSpy preEditSpy(m_clientTextInputV3, &TextInputV3::preedit_string); QSignalSpy commitStringSpy(m_clientTextInputV3, &TextInputV3::commit_string); QSignalSpy deleteSurroundingSpy(m_clientTextInputV3, &TextInputV3::delete_surrounding_text); - QSignalSpy doneSpy(m_clientTextInputV3, &TextInputV3::done); m_serverTextInputV3->sendPreEditString("Hello KDE community!", 1, 2); m_serverTextInputV3->deleteSurroundingText(6, 10); @@ -320,7 +322,7 @@ void TestTextInputV3Interface::testEvents() m_serverTextInputV3->done(); QVERIFY(doneSpy.wait()); - QCOMPARE(doneSpy.count(), 1); + QCOMPARE(doneSpy.count(), 2); QCOMPARE(preEditSpy.count(), 1); QCOMPARE(commitStringSpy.count(), 1); QCOMPARE(deleteSurroundingSpy.count(), 1); diff --git a/src/wayland/textinput_v3_interface.cpp b/src/wayland/textinput_v3_interface.cpp index 48bd139ea2..3d1ddaf967 100644 --- a/src/wayland/textinput_v3_interface.cpp +++ b/src/wayland/textinput_v3_interface.cpp @@ -209,6 +209,11 @@ void TextInputV3InterfacePrivate::sendPreEdit(const QString &text, const quint32 if (!surface) { return; } + + pending.preeditText = text; + pending.preeditCursorBegin = cursorBegin; + pending.preeditCursorEnd = cursorEnd; + const QList textInputs = enabledTextInputsForClient(surface->client()); for (auto resource : textInputs) { send_preedit_string(resource->handle, text, cursorBegin, cursorEnd); @@ -244,6 +249,11 @@ void TextInputV3InterfacePrivate::done() } const QList textInputs = enabledTextInputsForClient(surface->client()); + preeditText = pending.preeditText; + preeditCursorBegin = pending.preeditCursorBegin; + preeditCursorEnd = pending.preeditCursorEnd; + defaultPendingPreedit(); + for (auto resource : textInputs) { // zwp_text_input_v3.done takes the serial argument which is equal to number of commit requests issued send_done(resource->handle, serialHash[resource]); @@ -291,6 +301,9 @@ void TextInputV3InterfacePrivate::zwp_text_input_v3_disable(Resource *resource) // reset pending state to default Q_UNUSED(resource) defaultPending(); + preeditText = QString(); + preeditCursorBegin = 0; + preeditCursorEnd = 0; } void TextInputV3InterfacePrivate::zwp_text_input_v3_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor) @@ -377,6 +390,14 @@ void TextInputV3InterfacePrivate::zwp_text_input_v3_commit(Resource *resource) } Q_EMIT q->stateCommitted(serialHash[resource]); + + // Gtk text input implementation expect done to be sent after every commit to synchronize the serial value between commit() and done(). + // So we need to send the current preedit text with done(). + // If current preedit is empty, there is no need to send it. + if (!preeditText.isEmpty() || preeditCursorBegin != 0 || preeditCursorEnd != 0) { + send_preedit_string(resource->handle, preeditText, preeditCursorBegin, preeditCursorEnd); + } + send_done(resource->handle, serialHash[resource]); } void TextInputV3InterfacePrivate::defaultPending() @@ -389,6 +410,14 @@ void TextInputV3InterfacePrivate::defaultPending() pending.surroundingText = QString(); pending.surroundingTextCursorPosition = 0; pending.surroundingTextSelectionAnchor = 0; + defaultPendingPreedit(); +} + +void TextInputV3InterfacePrivate::defaultPendingPreedit() +{ + pending.preeditText = QString(); + pending.preeditCursorBegin = 0; + pending.preeditCursorEnd = 0; } TextInputV3Interface::TextInputV3Interface(SeatInterface *seat) diff --git a/src/wayland/textinput_v3_interface_p.h b/src/wayland/textinput_v3_interface_p.h index c404f33e10..50b383d3ce 100644 --- a/src/wayland/textinput_v3_interface_p.h +++ b/src/wayland/textinput_v3_interface_p.h @@ -63,6 +63,10 @@ public: qint32 surroundingTextSelectionAnchor = 0; TextInputChangeCause surroundingTextChangeCause = TextInputChangeCause::InputMethod; + QString preeditText; + quint32 preeditCursorBegin = 0; + quint32 preeditCursorEnd = 0; + struct { QRect cursorRectangle; @@ -73,12 +77,16 @@ public: QString surroundingText; qint32 surroundingTextCursorPosition = 0; qint32 surroundingTextSelectionAnchor = 0; + QString preeditText; + quint32 preeditCursorBegin = 0; + quint32 preeditCursorEnd = 0; } pending; QHash serialHash; QHash enabled; void defaultPending(); + void defaultPendingPreedit(); TextInputV3Interface *q;