Implement zwp_input_method_context_v1::grab_keyboard

Offers a new InputKeyboardV1Interface instance that wraps the
wl_keyboard and allows the compositor to forward the keyboard events.
This commit is contained in:
Aleix Pol 2021-05-31 20:14:58 +02:00
parent ecda92e023
commit 7dd092e910
3 changed files with 138 additions and 6 deletions

View file

@ -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 <linux/input-event-codes.h>
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"

View file

@ -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 <QHash>
#include <QTemporaryFile>
#include <unistd.h>
#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<QTemporaryFile> 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<char *>(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<InputMethodGrabV1> m_keyboardGrab;
QVector<Qt::KeyboardModifiers> 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
}
}

View file

@ -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<InputPanelSurfaceV1InterfacePrivate> 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<InputKeyboardV1InterfacePrivate> d;
};
}
Q_DECLARE_METATYPE(KWaylandServer::InputMethodV1Interface *)
Q_DECLARE_METATYPE(KWaylandServer::InputMethodGrabV1 *)