Commit 4651aa1d authored by Martin Flöser's avatar Martin Flöser

[wayland] Unset focused keyboard surface when handling key event internally

Summary:
So far when KWin intercepted a key event a leave was not sent to the
Wayland surface currently having keyboard focus. This could result in
the Wayland application to start repeating keys. E.g.

1. application gets key press event
2. This triggers an internal window to show
3. key release goes to KWin internal window
4. application starts to repeat key as there is no release

With this change whenever KWin intercepts the key event e.g. due to
 * internal window
 * Effects grabbing key event
 * Tabbox

the focused keyboard surface is set to null, thus triggering a leave
event and the client not starting to repeat the event.

Reviewers: #kwin, #plasma_on_wayland

Subscribers: plasma-devel, kwin

Tags: #plasma_on_wayland, #kwin

Differential Revision: https://phabricator.kde.org/D2402
parent 1111b9c9
......@@ -28,8 +28,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QPainter>
#include <QRasterWindow>
#include <KWayland/Client/keyboard.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/shell.h>
#include <linux/input.h>
using namespace KWayland::Client;
namespace KWin
{
......@@ -41,6 +48,7 @@ class InternalWindowTest : public QObject
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testEnterLeave();
void testPointerPressRelease();
void testPointerAxis();
......@@ -173,6 +181,13 @@ void InternalWindowTest::initTestCase()
void InternalWindowTest::init()
{
Cursor::setPos(QPoint(1280, 512));
QVERIFY(Test::setupWaylandConnection(s_socketName, Test::AdditionalWaylandInterface::Seat));
QVERIFY(Test::waitForWaylandKeyboard());
}
void InternalWindowTest::cleanup()
{
Test::destroyWaylandConnection();
}
void InternalWindowTest::testEnterLeave()
......@@ -285,6 +300,9 @@ void InternalWindowTest::testKeyboard()
QVERIFY(releaseSpy.isValid());
QVERIFY(clientAddedSpy.wait());
QCOMPARE(clientAddedSpy.count(), 1);
auto internalClient = clientAddedSpy.first().first().value<ShellClient*>();
QVERIFY(internalClient);
QVERIFY(internalClient->isInternal());
quint32 timestamp = 1;
QFETCH(QPoint, cursorPos);
......@@ -296,6 +314,51 @@ void InternalWindowTest::testKeyboard()
kwinApp()->platform()->keyboardKeyReleased(KEY_A, timestamp++);
QTRY_COMPARE(releaseSpy.count(), 1);
QCOMPARE(pressSpy.count(), 1);
// let's hide the window again and create a "real" window
win.hide();
clientAddedSpy.clear();
QScopedPointer<Keyboard> keyboard(Test::waylandSeat()->createKeyboard());
QVERIFY(!keyboard.isNull());
QVERIFY(keyboard->isValid());
QSignalSpy enteredSpy(keyboard.data(), &Keyboard::entered);
QVERIFY(enteredSpy.isValid());
QSignalSpy leftSpy(keyboard.data(), &Keyboard::left);
QVERIFY(leftSpy.isValid());
QScopedPointer<Surface> surface(Test::createSurface());
QScopedPointer<QObject> shellSurface(Test::createShellSurface(Test::ShellSurfaceType::WlShell, surface.data()));
// now let's render
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(c);
QVERIFY(c->isActive());
if (enteredSpy.isEmpty()) {
QVERIFY(enteredSpy.wait());
}
QCOMPARE(enteredSpy.count(), 1);
QSignalSpy windowShownSpy(internalClient, &ShellClient::windowShown);
QVERIFY(windowShownSpy.isValid());
win.show();
QCOMPARE(windowShownSpy.count(), 1);
QVERIFY(leftSpy.isEmpty());
QVERIFY(!leftSpy.wait(100));
// now let's trigger a key, which should result in a leave
kwinApp()->platform()->keyboardKeyPressed(KEY_A, timestamp++);
QVERIFY(leftSpy.wait());
QCOMPARE(pressSpy.count(), 2);
kwinApp()->platform()->keyboardKeyReleased(KEY_A, timestamp++);
QTRY_COMPARE(releaseSpy.count(), 2);
// after hiding the internal window, next key press should trigger an enter
win.hide();
kwinApp()->platform()->keyboardKeyPressed(KEY_A, timestamp++);
QVERIFY(enteredSpy.wait());
kwinApp()->platform()->keyboardKeyReleased(KEY_A, timestamp++);
}
void InternalWindowTest::testTouch()
......
......@@ -109,6 +109,7 @@ KWayland::Client::PlasmaWindowManagement *waylandWindowManagement();
bool waitForWaylandPointer();
bool waitForWaylandTouch();
bool waitForWaylandKeyboard();
void flushWaylandConnection();
......
......@@ -247,6 +247,18 @@ bool waitForWaylandTouch()
return hasTouchSpy.wait();
}
bool waitForWaylandKeyboard()
{
if (!s_waylandConnection.seat) {
return false;
}
QSignalSpy hasKeyboardSpy(s_waylandConnection.seat, &Seat::hasKeyboardChanged);
if (!hasKeyboardSpy.isValid()) {
return false;
}
return hasKeyboardSpy.wait();
}
void render(Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format)
{
QImage img(size, format);
......
......@@ -340,6 +340,7 @@ public:
if (!effects || !static_cast< EffectsHandlerImpl* >(effects)->hasKeyboardGrab()) {
return false;
}
waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
static_cast< EffectsHandlerImpl* >(effects)->grabbedKeyboardEvent(event);
return true;
}
......@@ -487,7 +488,11 @@ class InternalWindowEventFilter : public InputEventFilter {
return false;
}
event->setAccepted(false);
return QCoreApplication::sendEvent(found, event);
if (QCoreApplication::sendEvent(found, event)) {
waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
return true;
}
return false;
}
bool touchDown(quint32 id, const QPointF &pos, quint32 time) override {
......@@ -718,6 +723,7 @@ public:
if (!TabBox::TabBox::self() || !TabBox::TabBox::self()->isGrabbed()) {
return false;
}
waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
if (event->type() == QEvent::KeyPress)
TabBox::TabBox::self()->keyPress(event->modifiers() | event->key());
return true;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment