Invoke AbstractClient::enterEvent and ::leaveEvent on updating pointer window

This implements auto raise and focus follows mouse for Wayland.

The decoration interaction needs to be autotested.
This commit is contained in:
Martin Gräßlin 2016-02-18 13:02:07 +01:00
parent c6e6d9a872
commit b59593bd9d
2 changed files with 114 additions and 0 deletions

View file

@ -63,6 +63,7 @@ private Q_SLOTS:
void testModifierScrollOpacity_data();
void testModifierScrollOpacity();
void testScrollAction();
void testFocusFollowsMouse();
private:
void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50));
@ -518,6 +519,90 @@ void PointerInputTest::testScrollAction()
QTest::qWait(100);
}
void PointerInputTest::testFocusFollowsMouse()
{
using namespace KWayland::Client;
// need to create a pointer, otherwise it doesn't accept focus
auto pointer = m_seat->createPointer(m_seat);
QVERIFY(pointer);
QVERIFY(pointer->isValid());
// move cursor out of the way of first window to be created
Cursor::setPos(900, 900);
// first modify the config for this run
KConfigGroup group = kwinApp()->config()->group("Windows");
group.writeEntry("AutoRaise", true);
group.writeEntry("AutoRaiseInterval", 20);
group.writeEntry("DelayFocusInterval", 200);
group.writeEntry("FocusPolicy", "FocusFollowsMouse");
group.sync();
workspace()->slotReconfigure();
// verify the settings
QCOMPARE(options->focusPolicy(), Options::FocusFollowsMouse);
QVERIFY(options->isAutoRaise());
QCOMPARE(options->autoRaiseInterval(), 20);
QCOMPARE(options->delayFocusInterval(), 200);
// create two windows
QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded);
QVERIFY(clientAddedSpy.isValid());
Surface *surface1 = m_compositor->createSurface(m_compositor);
QVERIFY(surface1);
ShellSurface *shellSurface1 = m_shell->createSurface(surface1, surface1);
QVERIFY(shellSurface1);
render(surface1, QSize(800, 800));
QVERIFY(clientAddedSpy.wait());
AbstractClient *window1 = workspace()->activeClient();
QVERIFY(window1);
Surface *surface2 = m_compositor->createSurface(m_compositor);
QVERIFY(surface2);
ShellSurface *shellSurface2 = m_shell->createSurface(surface2, surface2);
QVERIFY(shellSurface2);
render(surface2, QSize(800, 800));
QVERIFY(clientAddedSpy.wait());
AbstractClient *window2 = workspace()->activeClient();
QVERIFY(window2);
QVERIFY(window1 != window2);
QCOMPARE(workspace()->topClientOnDesktop(1, -1), window2);
// geometry of the two windows should be overlapping
QVERIFY(window1->geometry().intersects(window2->geometry()));
// signal spies for active window changed and stacking order changed
QSignalSpy activeWindowChangedSpy(workspace(), &Workspace::clientActivated);
QVERIFY(activeWindowChangedSpy.isValid());
QSignalSpy stackingOrderChangedSpy(workspace(), &Workspace::stackingOrderChanged);
QVERIFY(stackingOrderChangedSpy.isValid());
QVERIFY(!window1->isActive());
QVERIFY(window2->isActive());
// move on top of first window
QVERIFY(window1->geometry().contains(10, 10));
QVERIFY(!window2->geometry().contains(10, 10));
Cursor::setPos(10, 10);
QVERIFY(stackingOrderChangedSpy.wait());
QCOMPARE(stackingOrderChangedSpy.count(), 1);
QCOMPARE(workspace()->topClientOnDesktop(1, -1), window1);
QTRY_VERIFY(window1->isActive());
// move on second window, but move away before active window change delay hits
Cursor::setPos(810, 810);
QVERIFY(stackingOrderChangedSpy.wait());
QCOMPARE(stackingOrderChangedSpy.count(), 2);
QCOMPARE(workspace()->topClientOnDesktop(1, -1), window2);
Cursor::setPos(10, 10);
QVERIFY(!activeWindowChangedSpy.wait(250));
QVERIFY(window1->isActive());
QCOMPARE(workspace()->topClientOnDesktop(1, -1), window1);
// as we moved back on window 1 that should been raised in the mean time
QCOMPARE(stackingOrderChangedSpy.count(), 3);
// quickly move on window 2 and back on window 1 should not raise window 2
Cursor::setPos(810, 810);
Cursor::setPos(10, 10);
QVERIFY(!stackingOrderChangedSpy.wait(250));
}
}
WAYLANDTEST_MAIN(KWin::PointerInputTest)

View file

@ -205,11 +205,15 @@ void PointerInputRedirection::update()
}
// TODO: handle pointer grab aka popups
Toplevel *t = m_input->findToplevel(m_pos.toPoint());
const auto oldDeco = m_decoration;
updateInternalWindow();
if (!m_internalWindow) {
updateDecoration(t);
} else {
// TODO: send hover leave to decoration
if (m_decoration) {
m_decoration->client()->leaveEvent();
}
m_decoration.clear();
}
if (m_decoration || m_internalWindow) {
@ -222,6 +226,9 @@ void PointerInputRedirection::update()
auto seat = waylandServer()->seat();
// disconnect old surface
if (oldWindow) {
if (AbstractClient *c = qobject_cast<AbstractClient*>(oldWindow.data())) {
c->leaveEvent();
}
disconnect(m_windowGeometryConnection);
m_windowGeometryConnection = QMetaObject::Connection();
if (auto p = seat->focusedPointer()) {
@ -230,6 +237,13 @@ void PointerInputRedirection::update()
}
}
}
if (AbstractClient *c = qobject_cast<AbstractClient*>(t)) {
// only send enter if it wasn't on deco for the same client before
if (m_decoration.isNull() || m_decoration->client() != c) {
c->enterEvent(m_pos.toPoint());
workspace()->updateFocusMousePosition(m_pos.toPoint());
}
}
if (t && t->surface()) {
seat->setFocusedPointerSurface(t->surface(), t->inputTransformation());
m_windowGeometryConnection = connect(t, &Toplevel::geometryChanged, this,
@ -343,7 +357,18 @@ void PointerInputRedirection::updateDecoration(Toplevel *t)
m_decoration.clear();
}
bool leftSend = false;
auto oldWindow = qobject_cast<AbstractClient*>(m_window.data());
if (oldWindow && (m_decoration && m_decoration->client() != oldWindow)) {
leftSend = true;
oldWindow->leaveEvent();
}
if (oldDeco && oldDeco != m_decoration) {
if (oldDeco->client() != t && !leftSend) {
leftSend = true;
oldDeco->client()->leaveEvent();
}
// send leave
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
QCoreApplication::instance()->sendEvent(oldDeco->decoration(), &event);
@ -352,6 +377,10 @@ void PointerInputRedirection::updateDecoration(Toplevel *t)
}
}
if (m_decoration) {
if (m_decoration->client() != oldWindow) {
m_decoration->client()->enterEvent(m_pos.toPoint());
workspace()->updateFocusMousePosition(m_pos.toPoint());
}
const QPointF p = m_pos - t->pos();
QHoverEvent event(QEvent::HoverMove, p, p);
QCoreApplication::instance()->sendEvent(m_decoration->decoration(), &event);