diff --git a/src/wayland/autotests/server/test_inputmethod_interface.cpp b/src/wayland/autotests/server/test_inputmethod_interface.cpp index f291767594..facf5f5855 100644 --- a/src/wayland/autotests/server/test_inputmethod_interface.cpp +++ b/src/wayland/autotests/server/test_inputmethod_interface.cpp @@ -20,11 +20,14 @@ #include "KWayland/Client/registry.h" #include "KWayland/Client/seat.h" #include "KWayland/Client/surface.h" +#include "KWayland/Client/keyboard.h" #include "KWayland/Client/output.h" #include "qwayland-input-method-unstable-v1.h" #include "qwayland-server-text-input-unstable-v1.h" +#include + using namespace KWaylandServer; class InputPanelSurface : public QObject, public QtWayland::zwp_input_panel_surface_v1 @@ -147,7 +150,7 @@ private Q_SLOTS: void testContentHints(); void testContentPurpose_data(); void testContentPurpose(); - + void testKeyboardGrab(); private: KWayland::Client::ConnectionThread *m_connection; KWayland::Client::EventQueue *m_queue; @@ -593,6 +596,30 @@ void TestInputMethodInterface::testContentPurpose() QVERIFY(!m_inputMethod->context()); } +void TestInputMethodInterface::testKeyboardGrab() +{ + QVERIFY(m_inputMethodIface); + QSignalSpy inputMethodActivateSpy(m_inputMethod, &InputMethodV1::activated); + + m_inputMethodIface->sendActivate(); + QVERIFY(inputMethodActivateSpy.wait()); + + QSignalSpy keyboardGrabSpy(m_inputMethodIface->context(), &InputMethodContextV1Interface::keyboardGrabRequested); + InputMethodV1Context *imContext = m_inputMethod->context(); + QVERIFY(imContext); + KWayland::Client::Keyboard *keyboard = new KWayland::Client::Keyboard(this); + keyboard->setup(imContext->grab_keyboard()); + QVERIFY(keyboard->isValid()); + QVERIFY(keyboardGrabSpy.count() || keyboardGrabSpy.wait()); + + QSignalSpy keyboardSpy(keyboard, &KWayland::Client::Keyboard::keyChanged); + m_inputMethodIface->context()->keyboardGrab()->sendKey(0, 0, KEY_F1, KeyboardKeyState::Pressed); + m_inputMethodIface->context()->keyboardGrab()->sendKey(0, 0, KEY_F1, KeyboardKeyState::Released); + keyboardSpy.wait(); + QCOMPARE(keyboardSpy.count(), 2); + + m_inputMethodIface->sendDeactivate(); +} QTEST_GUILESS_MAIN(TestInputMethodInterface) #include "test_inputmethod_interface.moc" diff --git a/src/wayland/inputmethod_v1_interface.cpp b/src/wayland/inputmethod_v1_interface.cpp index ab2c04cbb5..f523c3f14a 100644 --- a/src/wayland/inputmethod_v1_interface.cpp +++ b/src/wayland/inputmethod_v1_interface.cpp @@ -7,20 +7,88 @@ #include "inputmethod_v1_interface.h" #include "seat_interface.h" #include "display.h" +#include "keyboard_interface.h" +#include "keyboard_interface_p.h" #include "surface_interface.h" #include "output_interface.h" #include "surfacerole_p.h" +#include "logging.h" #include +#include + +#include #include "qwayland-server-input-method-unstable-v1.h" #include "qwayland-server-text-input-unstable-v1.h" +#include "qwayland-server-wayland.h" namespace KWaylandServer { static int s_version = 1; +class InputKeyboardV1InterfacePrivate : public QtWaylandServer::wl_keyboard +{ +public: + InputKeyboardV1InterfacePrivate() + {} +}; + +InputMethodGrabV1::InputMethodGrabV1(QObject *parent) + : QObject(parent) + , d(new InputKeyboardV1InterfacePrivate) +{} + +InputMethodGrabV1::~InputMethodGrabV1() +{ +} + +void InputMethodGrabV1::sendKeymap(const QByteArray &keymap) +{ + QScopedPointer tmp(new QTemporaryFile()); + if (!tmp->open()) { + qCWarning(KWAYLAND_SERVER) << "Failed to create keymap file:" << tmp->errorString(); + return; + } + + unlink(tmp->fileName().toUtf8().constData()); + if (!tmp->resize(keymap.size())) { + qCWarning(KWAYLAND_SERVER) << "Failed to resize keymap file:" << tmp->errorString(); + return; + } + + uchar *address = tmp->map(0, keymap.size()); + if (!address) { + qCWarning(KWAYLAND_SERVER) << "Failed to map keymap file:" << tmp->errorString(); + return; + } + + qstrncpy(reinterpret_cast(address), keymap.constData(), keymap.size() + 1); + tmp->unmap(address); + + const auto resources = d->resourceMap(); + for (auto r : resources) { + d->send_keymap(r->handle, QtWaylandServer::wl_keyboard::keymap_format::keymap_format_xkb_v1, tmp->handle(), tmp->size()); + } +} + +void InputMethodGrabV1::sendKey(quint32 serial, quint32 timestamp, quint32 key, KeyboardKeyState state) +{ + const auto resources = d->resourceMap(); + for (auto r : resources) { + d->send_key(r->handle, serial, timestamp, key, quint32(state)); + } +} + +void InputMethodGrabV1::sendModifiers(quint32 serial, quint32 depressed, quint32 latched, quint32 locked, quint32 group) +{ + const auto resources = d->resourceMap(); + for (auto r : resources) { + d->send_modifiers(r->handle, depressed, latched, locked, group, serial); + } +} + class InputMethodContextV1InterfacePrivate : public QtWaylandServer::zwp_input_method_context_v1 { public: @@ -30,6 +98,8 @@ public: { } + ~InputMethodContextV1InterfacePrivate() {} + void zwp_input_method_context_v1_commit_string(Resource *, uint32_t serial, const QString &text) override { Q_EMIT q->commitString(serial, text); @@ -80,9 +150,11 @@ public: { Q_EMIT q->keysym(serial, time, sym, state == WL_KEYBOARD_KEY_STATE_PRESSED, toQtModifiers(modifiers)); } - void zwp_input_method_context_v1_grab_keyboard(Resource *, uint32_t keyboard) override + void zwp_input_method_context_v1_grab_keyboard(Resource *resource, uint32_t id) override { - Q_EMIT q->grabKeyboard(keyboard); + m_keyboardGrab.reset(new InputMethodGrabV1(q)); + m_keyboardGrab->d->add(resource->client(), id, 1); + Q_EMIT q->keyboardGrabRequested(m_keyboardGrab.data()); } void zwp_input_method_context_v1_key(Resource *, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) override { @@ -131,8 +203,8 @@ public: wl_resource_destroy(resource->handle); } -private: InputMethodContextV1Interface *const q; + QScopedPointer m_keyboardGrab; QVector mods; }; @@ -265,6 +337,11 @@ void InputMethodContextV1Interface::sendSurroundingText(const QString &text, uin } } +InputMethodGrabV1 *InputMethodContextV1Interface::keyboardGrab() const +{ + return d->m_keyboardGrab.get(); +} + class InputPanelSurfaceV1InterfacePrivate : public QtWaylandServer::zwp_input_panel_surface_v1, public SurfaceRole { friend class InputPanelSurfaceV1Interface; @@ -417,3 +494,4 @@ InputMethodContextV1Interface *InputMethodV1Interface::context() const } } + diff --git a/src/wayland/inputmethod_v1_interface.h b/src/wayland/inputmethod_v1_interface.h index 1c052c649b..c4d64bb026 100644 --- a/src/wayland/inputmethod_v1_interface.h +++ b/src/wayland/inputmethod_v1_interface.h @@ -18,6 +18,7 @@ namespace KWaylandServer class OutputInterface; class SurfaceInterface; class Display; +class KeyboardInterface; class InputPanelSurfaceV1Interface; class InputMethodContextV1Interface; @@ -25,6 +26,10 @@ class InputMethodV1InterfacePrivate; class InputMethodContextV1InterfacePrivate; class InputPanelV1InterfacePrivate; class InputPanelSurfaceV1InterfacePrivate; +class InputMethodGrabV1; +class InputKeyboardV1InterfacePrivate; + +enum class KeyboardKeyState : quint32; //This file's classes implment input_method_unstable_v1 @@ -73,6 +78,8 @@ public: void sendCommitState(quint32 serial); void sendPreferredLanguage(const QString &language); + InputMethodGrabV1 *keyboardGrab() const; + Q_SIGNALS: void commitString(quint32 serial, const QString &text); void preeditString(quint32 serial, const QString &text, const QString &commit); @@ -81,11 +88,11 @@ Q_SIGNALS: void deleteSurroundingText(qint32 index, quint32 length); void cursorPosition(qint32 index, qint32 anchor); void keysym(quint32 serial, quint32 time, quint32 sym, bool pressed, Qt::KeyboardModifiers modifiers); - void grabKeyboard(quint32 keyboard); void key(quint32 serial, quint32 time, quint32 key, bool pressed); void modifiers(quint32 serial, quint32 mods_depressed, quint32 mods_latched, quint32 mods_locked, quint32 group); void language(quint32 serial, const QString &language); void textDirection(quint32 serial, Qt::LayoutDirection direction); + void keyboardGrabRequested(InputMethodGrabV1 *keyboardGrab); private: friend class InputMethodV1Interface; @@ -125,7 +132,6 @@ public: }; Q_ENUM(Position) - quint32 id() const; SurfaceInterface *surface() const; Q_SIGNALS: @@ -138,6 +144,27 @@ private: QScopedPointer d; }; +/** + * Implements a wl_keyboard tailored for zwp_input_method_v1 use-cases + */ +class KWAYLANDSERVER_EXPORT InputMethodGrabV1 : public QObject +{ + Q_OBJECT +public: + ~InputMethodGrabV1() override; + + void sendKeymap(const QByteArray &content); + void sendKey(quint32 serial, quint32 timestamp, quint32 key, KeyboardKeyState state); + void sendModifiers(quint32 serial, quint32 depressed, quint32 latched, quint32 locked, quint32 group); + +private: + InputMethodGrabV1(QObject *parent); + friend class InputPanelV1InterfacePrivate; + friend class InputMethodContextV1InterfacePrivate; + QScopedPointer d; +}; + } Q_DECLARE_METATYPE(KWaylandServer::InputMethodV1Interface *) +Q_DECLARE_METATYPE(KWaylandServer::InputMethodGrabV1 *)