wayland: send modifiers to clients under the mouse

This commit is contained in:
Xaver Hugl 2022-08-09 04:10:45 +02:00
parent ca9eb3d87d
commit 9915cfc6df
5 changed files with 106 additions and 10 deletions

View file

@ -23,10 +23,12 @@
#include "wayland_server.h" #include "wayland_server.h"
#include "window.h" #include "window.h"
#include "workspace.h" #include "workspace.h"
#include "x11window.h"
#include <KWayland/Client/buffer.h> #include <KWayland/Client/buffer.h>
#include <KWayland/Client/compositor.h> #include <KWayland/Client/compositor.h>
#include <KWayland/Client/connection_thread.h> #include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/keyboard.h>
#include <KWayland/Client/pointer.h> #include <KWayland/Client/pointer.h>
#include <KWayland/Client/region.h> #include <KWayland/Client/region.h>
#include <KWayland/Client/seat.h> #include <KWayland/Client/seat.h>
@ -35,6 +37,7 @@
#include <KWayland/Client/surface.h> #include <KWayland/Client/surface.h>
#include <linux/input.h> #include <linux/input.h>
#include <xcb/xcb_icccm.h>
namespace KWin namespace KWin
{ {
@ -121,6 +124,7 @@ private Q_SLOTS:
void testHideShowCursor(); void testHideShowCursor();
void testDefaultInputRegion(); void testDefaultInputRegion();
void testEmptyInputRegion(); void testEmptyInputRegion();
void testUnfocusedModifiers();
private: private:
void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50)); void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50));
@ -1817,6 +1821,69 @@ void PointerInputTest::testEmptyInputRegion()
QVERIFY(Test::waitForWindowDestroyed(window)); QVERIFY(Test::waitForWindowDestroyed(window));
} }
void PointerInputTest::testUnfocusedModifiers()
{
// This test verifies that a window under the cursor gets modifier events,
// even if it isn't focused
// create a Wayland window
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
QVERIFY(surface != nullptr);
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
QVERIFY(shellSurface != nullptr);
Window *waylandWindow = Test::renderAndWaitForShown(surface.get(), QSize(10, 10), Qt::blue);
QVERIFY(waylandWindow);
waylandWindow->move(QPoint(0, 0));
Test::waitForWaylandKeyboard();
std::unique_ptr<KWayland::Client::Keyboard> keyboard(Test::waylandSeat()->createKeyboard());
// Create an xcb window.
Test::XcbConnectionPtr c = Test::createX11Connection();
QVERIFY(!xcb_connection_has_error(c.get()));
const QRect windowGeometry(0, 0, 10, 10);
xcb_window_t windowId = xcb_generate_id(c.get());
xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
windowGeometry.x(),
windowGeometry.y(),
windowGeometry.width(),
windowGeometry.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
xcb_size_hints_t hints = {};
xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
xcb_map_window(c.get(), windowId);
xcb_flush(c.get());
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
QVERIFY(windowCreatedSpy.wait());
X11Window *x11window = windowCreatedSpy.last().first().value<X11Window *>();
QVERIFY(waylandWindow);
x11window->move(QPoint(10, 10));
workspace()->activateWindow(x11window, true);
// Move the pointer over the now unfocused Wayland window
input()->pointer()->warp(waylandWindow->frameGeometry().center());
QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), waylandWindow->surface());
QSignalSpy spy(keyboard.get(), &KWayland::Client::Keyboard::modifiersChanged);
Test::keyboardKeyPressed(KEY_LEFTCTRL, 1);
QVERIFY(spy.wait(50));
QCOMPARE(spy.last().at(0).toInt(), XCB_MOD_MASK_CONTROL);
Test::keyboardKeyReleased(KEY_LEFTCTRL, 2);
// Destroy the x11 window.
QSignalSpy windowClosedSpy(waylandWindow, &X11Window::closed);
xcb_unmap_window(c.get(), windowId);
xcb_destroy_window(c.get(), windowId);
xcb_flush(c.get());
c.reset();
// Destroy the Wayland window.
shellSurface.reset();
QVERIFY(Test::waitForWindowDestroyed(waylandWindow));
}
} }
WAYLANDTEST_MAIN(KWin::PointerInputTest) WAYLANDTEST_MAIN(KWin::PointerInputTest)

View file

@ -100,9 +100,9 @@ void KeyboardInterface::setKeymap(const QByteArray &content)
} }
} }
void KeyboardInterfacePrivate::sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial) void KeyboardInterfacePrivate::sendModifiers(SurfaceInterface *surface, quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial)
{ {
const QList<Resource *> keyboards = keyboardsForClient(focusedSurface->client()); const QList<Resource *> keyboards = keyboardsForClient(surface->client());
for (Resource *keyboardResource : keyboards) { for (Resource *keyboardResource : keyboards) {
send_modifiers(keyboardResource->handle, serial, depressed, latched, locked, group); send_modifiers(keyboardResource->handle, serial, depressed, latched, locked, group);
} }
@ -129,9 +129,9 @@ KeyboardInterface::KeyboardInterface(SeatInterface *seat)
KeyboardInterface::~KeyboardInterface() = default; KeyboardInterface::~KeyboardInterface() = default;
void KeyboardInterfacePrivate::sendModifiers() void KeyboardInterfacePrivate::sendModifiers(SurfaceInterface *surface)
{ {
sendModifiers(modifiers.depressed, modifiers.latched, modifiers.locked, modifiers.group, modifiers.serial); sendModifiers(surface, modifiers.depressed, modifiers.latched, modifiers.locked, modifiers.group, modifiers.serial);
} }
void KeyboardInterface::setFocusedSurface(SurfaceInterface *surface, quint32 serial) void KeyboardInterface::setFocusedSurface(SurfaceInterface *surface, quint32 serial)
@ -155,7 +155,19 @@ void KeyboardInterface::setFocusedSurface(SurfaceInterface *surface, quint32 ser
}); });
d->sendEnter(d->focusedSurface, serial); d->sendEnter(d->focusedSurface, serial);
d->sendModifiers(); d->sendModifiers(d->focusedSurface);
}
void KeyboardInterface::setModifierFocusSurface(SurfaceInterface *surface)
{
if (d->modifierFocusSurface == surface) {
return;
}
d->modifierFocusSurface = surface;
if (d->modifierFocusSurface && d->focusedSurface != d->modifierFocusSurface) {
d->modifiers.serial = d->seat->display()->nextSerial();
d->sendModifiers(d->modifierFocusSurface);
}
} }
QVector<quint32> KeyboardInterfacePrivate::pressedKeys() const QVector<quint32> KeyboardInterfacePrivate::pressedKeys() const
@ -210,12 +222,18 @@ void KeyboardInterface::sendModifiers(quint32 depressed, quint32 latched, quint3
d->modifiers.group = group; d->modifiers.group = group;
changed = true; changed = true;
} }
if (!changed || !d->focusedSurface) { if (!changed) {
return; return;
} }
d->modifiers.serial = d->seat->display()->nextSerial(); if (d->focusedSurface) {
d->sendModifiers(depressed, latched, locked, group, d->modifiers.serial); d->modifiers.serial = d->seat->display()->nextSerial();
d->sendModifiers(d->focusedSurface, depressed, latched, locked, group, d->modifiers.serial);
}
if (d->modifierFocusSurface && d->focusedSurface != d->modifierFocusSurface) {
d->modifiers.serial = d->seat->display()->nextSerial();
d->sendModifiers(d->modifierFocusSurface, depressed, latched, locked, group, d->modifiers.serial);
}
} }
void KeyboardInterface::setRepeatInfo(qint32 charactersPerSecond, qint32 delay) void KeyboardInterface::setRepeatInfo(qint32 charactersPerSecond, qint32 delay)

View file

@ -60,6 +60,7 @@ public:
private: private:
void setFocusedSurface(SurfaceInterface *surface, quint32 serial); void setFocusedSurface(SurfaceInterface *surface, quint32 serial);
void setModifierFocusSurface(SurfaceInterface *surface);
friend class SeatInterface; friend class SeatInterface;
friend class SeatInterfacePrivate; friend class SeatInterfacePrivate;
friend class KeyboardInterfacePrivate; friend class KeyboardInterfacePrivate;

View file

@ -23,8 +23,8 @@ public:
KeyboardInterfacePrivate(SeatInterface *s); KeyboardInterfacePrivate(SeatInterface *s);
void sendKeymap(Resource *resource); void sendKeymap(Resource *resource);
void sendModifiers(); void sendModifiers(SurfaceInterface *surface);
void sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial); void sendModifiers(SurfaceInterface *surface, quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial);
QList<Resource *> keyboardsForClient(ClientConnection *client) const; QList<Resource *> keyboardsForClient(ClientConnection *client) const;
void sendLeave(SurfaceInterface *surface, quint32 serial); void sendLeave(SurfaceInterface *surface, quint32 serial);
@ -38,6 +38,7 @@ public:
SeatInterface *seat; SeatInterface *seat;
SurfaceInterface *focusedSurface = nullptr; SurfaceInterface *focusedSurface = nullptr;
QMetaObject::Connection destroyConnection; QMetaObject::Connection destroyConnection;
QPointer<SurfaceInterface> modifierFocusSurface;
QByteArray keymap; QByteArray keymap;
KWin::RamFile sharedKeymapFile; KWin::RamFile sharedKeymapFile;

View file

@ -461,6 +461,9 @@ void SeatInterface::notifyPointerMotion(const QPointF &pos)
if (d->pointer->focusedSurface() != effectiveFocusedSurface) { if (d->pointer->focusedSurface() != effectiveFocusedSurface) {
d->pointer->sendEnter(effectiveFocusedSurface, localPosition, display()->nextSerial()); d->pointer->sendEnter(effectiveFocusedSurface, localPosition, display()->nextSerial());
if (d->keyboard) {
d->keyboard->setModifierFocusSurface(effectiveFocusedSurface);
}
} }
d->pointer->sendMotion(localPosition); d->pointer->sendMotion(localPosition);
@ -580,6 +583,9 @@ void SeatInterface::notifyPointerEnter(SurfaceInterface *surface, const QPointF
localPosition = surface->mapToChild(effectiveFocusedSurface, localPosition); localPosition = surface->mapToChild(effectiveFocusedSurface, localPosition);
} }
d->pointer->sendEnter(effectiveFocusedSurface, localPosition, serial); d->pointer->sendEnter(effectiveFocusedSurface, localPosition, serial);
if (d->keyboard) {
d->keyboard->setModifierFocusSurface(effectiveFocusedSurface);
}
} }
void SeatInterface::notifyPointerLeave() void SeatInterface::notifyPointerLeave()
@ -599,6 +605,9 @@ void SeatInterface::notifyPointerLeave()
const quint32 serial = d->display->nextSerial(); const quint32 serial = d->display->nextSerial();
d->pointer->sendLeave(serial); d->pointer->sendLeave(serial);
if (d->keyboard) {
d->keyboard->setModifierFocusSurface(nullptr);
}
} }
void SeatInterface::setFocusedPointerSurfacePosition(const QPointF &surfacePosition) void SeatInterface::setFocusedPointerSurfacePosition(const QPointF &surfacePosition)