diff --git a/autotests/client/test_wayland_seat.cpp b/autotests/client/test_wayland_seat.cpp --- a/autotests/client/test_wayland_seat.cpp +++ b/autotests/client/test_wayland_seat.cpp @@ -77,7 +77,8 @@ void testTouch(); void testDisconnect(); void testPointerEnterOnUnboundSurface(); - // TODO: add test for keymap + void testKeymap(); + void testKeymapThroughFd(); private: KWayland::Server::Display *m_display; @@ -1699,7 +1700,6 @@ m_seatInterface->setKeyRepeatInfo(30, 560); m_seatInterface->setKeyRepeatInfo(25, 660); m_seatInterface->updateKeyboardModifiers(5, 6, 7, 8); - m_seatInterface->setKeymap(open("/dev/null", O_RDONLY), 0); m_seatInterface->setFocusedKeyboardSurface(nullptr); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); @@ -2392,5 +2392,97 @@ QVERIFY(!clientErrorSpy.wait(100)); } +void TestWaylandSeat::testKeymap() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + + m_seatInterface->setHasKeyboard(true); + QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); + QVERIFY(keyboardChangedSpy.isValid()); + QVERIFY(keyboardChangedSpy.wait()); + + QScopedPointer keyboard(m_seat->createKeyboard()); + QSignalSpy keymapChangedSpy(keyboard.data(), &Keyboard::keymapChanged); + QVERIFY(keymapChangedSpy.isValid()); + + m_seatInterface->setKeymapData(QByteArrayLiteral("foo")); + QVERIFY(keymapChangedSpy.wait()); + int fd = keymapChangedSpy.first().first().toInt(); + QVERIFY(fd != -1); + QCOMPARE(keymapChangedSpy.first().last().value(), 3u); + QFile file; + QVERIFY(file.open(fd, QIODevice::ReadOnly)); + const char *address = reinterpret_cast(file.map(0, keymapChangedSpy.first().last().value())); + QVERIFY(address); + QCOMPARE(qstrcmp(address, "foo"), 0); + file.close(); + + // change the keymap + keymapChangedSpy.clear(); + m_seatInterface->setKeymapData(QByteArrayLiteral("bar")); + QVERIFY(keymapChangedSpy.wait()); + fd = keymapChangedSpy.first().first().toInt(); + QVERIFY(fd != -1); + QCOMPARE(keymapChangedSpy.first().last().value(), 3u); + QVERIFY(file.open(fd, QIODevice::ReadWrite));address = reinterpret_cast(file.map(0, keymapChangedSpy.first().last().value())); + QVERIFY(address); + QCOMPARE(qstrcmp(address, "bar"), 0); +} + +void TestWaylandSeat::testKeymapThroughFd() +{ +#if KWAYLANDSERVER_BUILD_DEPRECATED_SINCE(5, 69) + using namespace KWayland::Client; + using namespace KWayland::Server; + + m_seatInterface->setHasKeyboard(true); + QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); + QVERIFY(keyboardChangedSpy.isValid()); + QVERIFY(keyboardChangedSpy.wait()); + + QScopedPointer keyboard(m_seat->createKeyboard()); + QSignalSpy keymapChangedSpy(keyboard.data(), &Keyboard::keymapChanged); + QVERIFY(keymapChangedSpy.isValid()); + + QTemporaryFile serverFile; + QVERIFY(serverFile.open()); + QByteArray data = QByteArrayLiteral("foobar"); + QVERIFY(serverFile.resize(data.size() + 1)); + uchar *serverAddress = serverFile.map(0, data.size() + 1); + QVERIFY(serverAddress); + QVERIFY(qstrncpy(reinterpret_cast(serverAddress), data.constData(), data.size() + 1)); + m_seatInterface->setKeymap(serverFile.handle(), data.size()); + + + QVERIFY(keymapChangedSpy.wait()); + int fd = keymapChangedSpy.first().first().toInt(); + QVERIFY(fd != -1); + QCOMPARE(keymapChangedSpy.first().last().value(), 6u); + QFile file; + QVERIFY(file.open(fd, QIODevice::ReadOnly)); + const char *address = reinterpret_cast(file.map(0, keymapChangedSpy.first().last().value())); + QVERIFY(address); + QCOMPARE(qstrcmp(address, "foobar"), 0); + + QScopedPointer keyboard2(m_seat->createKeyboard()); + QSignalSpy keymapChangedSpy2(keyboard2.data(), &Keyboard::keymapChanged); + QVERIFY(keymapChangedSpy2.isValid()); + QVERIFY(keymapChangedSpy2.wait()); + + int fd2 = keymapChangedSpy2.first().first().toInt(); + QVERIFY(fd2 != -1); + QCOMPARE(keymapChangedSpy2.first().last().value(), 6u); + QFile file2; + QVERIFY(file2.open(fd2, QIODevice::ReadWrite)); + char *address2 = reinterpret_cast(file2.map(0, keymapChangedSpy2.first().last().value())); + QVERIFY(address2); + QCOMPARE(qstrcmp(address2, "foobar"), 0); + address2[0] = 'g'; + QCOMPARE(qstrcmp(address2, "goobar"), 0); + QCOMPARE(qstrcmp(address, "foobar"), 0); +#endif +} + QTEST_GUILESS_MAIN(TestWaylandSeat) #include "test_wayland_seat.moc" diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -299,7 +299,7 @@ GROUP_BASE_NAME KF VERSION ${KF5_VERSION} DEPRECATED_BASE_VERSION 0 - DEPRECATION_VERSIONS 5.5 5.28 5.50 5.52 + DEPRECATION_VERSIONS 5.5 5.28 5.50 5.52 5.69 ) # TODO: add support for EXCLUDE_DEPRECATED_BEFORE_AND_AT to all KWayland libs # needs fixing of undeprecated API being still implemented using own deprecated API diff --git a/src/server/keyboard_interface.h b/src/server/keyboard_interface.h --- a/src/server/keyboard_interface.h +++ b/src/server/keyboard_interface.h @@ -36,6 +36,7 @@ private: void setFocusedSurface(SurfaceInterface *surface, quint32 serial); void setKeymap(int fd, quint32 size); + void setKeymap(const QByteArray &content); void updateModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial); void keyPressed(quint32 key, quint32 serial); void keyReleased(quint32 key, quint32 serial); diff --git a/src/server/keyboard_interface.cpp b/src/server/keyboard_interface.cpp --- a/src/server/keyboard_interface.cpp +++ b/src/server/keyboard_interface.cpp @@ -9,10 +9,13 @@ #include "seat_interface.h" #include "surface_interface.h" // Qt +#include #include // Wayland #include +#include + namespace KWayland { @@ -76,6 +79,29 @@ d->sendKeymap(fd, size); } +void KeyboardInterface::setKeymap(const QByteArray &content) +{ + QScopedPointer tmp{new QTemporaryFile(this)}; + if (!tmp->open()) { + return; + } + unlink(tmp->fileName().toUtf8().constData()); + if (!tmp->resize(content.size())) { + return; + } + uchar *address = tmp->map(0, content.size()); + if (!address) { + return; + } + if (qstrncpy(reinterpret_cast(address), content.constData(), content.size() + 1) == nullptr) { + return; + } + tmp->unmap(address); + Q_D(); + d->sendKeymap(tmp->handle(), content.size()); + d->keymap.swap(tmp); +} + void KeyboardInterface::Private::sendKeymap(int fd, quint32 size) { if (!resource) { diff --git a/src/server/keyboard_interface_p.h b/src/server/keyboard_interface_p.h --- a/src/server/keyboard_interface_p.h +++ b/src/server/keyboard_interface_p.h @@ -10,6 +10,8 @@ #include +class QTemporaryFile; + namespace KWayland { namespace Server @@ -31,6 +33,7 @@ SurfaceInterface *focusedSurface = nullptr; QPointer focusedChildSurface; QMetaObject::Connection destroyConnection; + QScopedPointer keymap; private: static const struct wl_keyboard_interface s_interface; diff --git a/src/server/seat_interface.h b/src/server/seat_interface.h --- a/src/server/seat_interface.h +++ b/src/server/seat_interface.h @@ -546,7 +546,19 @@ * @name keyboard related methods **/ ///@{ +#if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 69) + /** + * @deprecated since 5.69, use setKeymapData + **/ + KWAYLANDSERVER_DEPRECATED_VERSION(5, 69, "Use SeatInterface::setKeymapData()") void setKeymap(int fd, quint32 size); +#endif + /** + * Sets the xkb keymap with @p content for this Seat. + * The content gets sent to all registered KeyboardInterfaces + * @since 5.69 + **/ + void setKeymapData(const QByteArray &content); void keyPressed(quint32 key); void keyReleased(quint32 key); void updateKeyboardModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group); @@ -568,8 +580,16 @@ quint32 lockedModifiers() const; quint32 groupModifiers() const; quint32 lastModifiersSerial() const; - int keymapFileDescriptor() const; - quint32 keymapSize() const; +#if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 69) + /** + * @deprecated since 5.69 + **/ + int KWAYLANDSERVER_DEPRECATED keymapFileDescriptor() const; + /** + * @deprecated since 5.69 + **/ + quint32 KWAYLANDSERVER_DEPRECATED keymapSize() const; +#endif bool isKeymapXkbCompatible() const; QVector pressedKeys() const; /** diff --git a/src/server/seat_interface.cpp b/src/server/seat_interface.cpp --- a/src/server/seat_interface.cpp +++ b/src/server/seat_interface.cpp @@ -14,6 +14,8 @@ #include "pointer_interface_p.h" #include "surface_interface.h" #include "textinput_interface_p.h" +// Qt +#include // Wayland #ifndef WL_SEAT_NAME_SINCE_VERSION #define WL_SEAT_NAME_SINCE_VERSION 2 @@ -522,7 +524,7 @@ } keyboard->repeatInfo(keys.keyRepeat.charactersPerSecond, keys.keyRepeat.delay); if (keys.keymap.xkbcommonCompatible) { - keyboard->setKeymap(keys.keymap.fd, keys.keymap.size); + keyboard->setKeymap(keys.keymap.content); } keyboards << keyboard; if (keys.focus.surface && keys.focus.surface->client() == clientConnection) { @@ -1147,14 +1149,28 @@ } } +#if KWAYLANDSERVER_BUILD_DEPRECATED_SINCE(5, 69) void SeatInterface::setKeymap(int fd, quint32 size) +{ + QFile file; + if (!file.open(fd, QIODevice::ReadOnly)) { + return; + } + const char *address = reinterpret_cast(file.map(0, size)); + if (!address) { + return; + } + setKeymapData(QByteArray(address, size)); +} +#endif + +void SeatInterface::setKeymapData(const QByteArray &content) { Q_D(); d->keys.keymap.xkbcommonCompatible = true; - d->keys.keymap.fd = fd; - d->keys.keymap.size = size; + d->keys.keymap.content = content; for (auto it = d->keyboards.constBegin(); it != d->keyboards.constEnd(); ++it) { - (*it)->setKeymap(fd, size); + (*it)->setKeymap(content); } } @@ -1211,17 +1227,19 @@ return d->keys.keymap.xkbcommonCompatible; } +#if KWAYLANDSERVER_BUILD_DEPRECATED_SINCE(5, 69) int SeatInterface::keymapFileDescriptor() const { - Q_D(); - return d->keys.keymap.fd; + return -1; } +#endif +#if KWAYLANDSERVER_BUILD_DEPRECATED_SINCE(5, 69) quint32 SeatInterface::keymapSize() const { - Q_D(); - return d->keys.keymap.size; + return 0; } +#endif quint32 SeatInterface::depressedModifiers() const { diff --git a/src/server/seat_interface_p.h b/src/server/seat_interface_p.h --- a/src/server/seat_interface_p.h +++ b/src/server/seat_interface_p.h @@ -86,9 +86,8 @@ }; QHash states; struct Keymap { - int fd = -1; - quint32 size = 0; bool xkbcommonCompatible = false; + QByteArray content; }; Keymap keymap; struct Modifiers {