Improve lock screen interaction for pointer in InputRedirection

InputRedirection connects to lockStateChanged to udate the current
pointer window. This way we can ensure that the current pointer
surface gets reset as soon as the screen locks (c.f. the expect
fail in the autotest) and also that it restores to the surface under
the mouse once the screen is unlocked.

The relevant code was not yet lock screen aware and performed an
early exit. Part of the code was fine, e.g. findToplevel is lock
screen aware. So this change adjusts the methods for updating the
internal window and decoration to be lock screen aware, that is they
get reset. With that updatePointerWindow is also lock screen aware.

Thus the LockScreenFilter can also use updatePointerWindow just like
the normal handling and does not need to reimplement parts of it. As
it now relies on other code being correct it has an additional check
to verify that the current pointer surface is a surface which is allowed
to get events. If it isn't the events are not forwarded.

Reviewed-By: Bhushan Shah
This commit is contained in:
Martin Gräßlin 2016-02-09 17:18:58 +01:00
parent 142e826191
commit 16a33f662b
2 changed files with 41 additions and 32 deletions

View file

@ -300,16 +300,13 @@ void LockScreenTest::testPointer()
LOCK
QEXPECT_FAIL("", "Adding the lock screen window should send left event", Continue);
QVERIFY(leftSpy.wait(100));
QEXPECT_FAIL("", "Adding the lock screen window should send left event", Continue);
QCOMPARE(leftSpy.count(), 1);
// simulate moving out in and out again
MOTION(c->geometry().center());
MOTION(c->geometry().bottomRight() + QPoint(100, 100));
MOTION(c->geometry().bottomRight() + QPoint(100, 100));
QEXPECT_FAIL("", "Adding the lock screen window should send left event", Continue);
QVERIFY(!leftSpy.wait(100));
QCOMPARE(leftSpy.count(), 1);
QCOMPARE(enteredSpy.count(), 1);
@ -319,16 +316,14 @@ void LockScreenTest::testPointer()
// and unlock
UNLOCK
QEXPECT_FAIL("", "Focus doesn't move back on surface removal", Continue);
QVERIFY(enteredSpy.wait(100));
QEXPECT_FAIL("", "Focus doesn't move back on surface removal", Continue);
QCOMPARE(enteredSpy.count(), 2);
// move on the window
MOTION(c->geometry().center() + QPoint(100, 100));
QVERIFY(leftSpy.wait());
MOTION(c->geometry().center());
QEXPECT_FAIL("", "Focus doesn't move back on surface removal", Continue);
QVERIFY(!enteredSpy.wait(100));
QCOMPARE(enteredSpy.count(), 2);
QVERIFY(enteredSpy.wait());
QCOMPARE(enteredSpy.count(), 3);
}
void LockScreenTest::testPointerButton()

View file

@ -49,6 +49,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QTemporaryFile>
// KDE
#include <kkeyserver.h>
//screenlocker
#include <KScreenLocker/KsldApp>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h>
// system
@ -352,19 +355,16 @@ public:
auto seat = waylandServer()->seat();
seat->setTimestamp(event->timestamp());
if (event->type() == QEvent::MouseMove) {
Toplevel *t = input()->findToplevel(event->screenPos().toPoint());
if (t && t->surface()) {
seat->setFocusedPointerSurface(t->surface(), t->inputTransformation());
} else {
seat->setFocusedPointerSurface(nullptr);
if (event->buttons() == Qt::NoButton) {
// update pointer window only if no button is pressed
input()->updatePointerWindow();
}
if (pointerSurfaceAllowed()) {
seat->setPointerPos(event->screenPos().toPoint());
}
seat->setPointerPos(event->screenPos().toPoint());
} else if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) {
if (KWayland::Server::SurfaceInterface *s = seat->focusedPointerSurface()) {
Toplevel *t = waylandServer()->findClient(s);
if (t->isLockScreen() || t->isInputMethod()) {
event->type() == QEvent::MouseButtonPress ? seat->pointerButtonPressed(nativeButton) : seat->pointerButtonReleased(nativeButton);
}
if (pointerSurfaceAllowed()) {
event->type() == QEvent::MouseButtonPress ? seat->pointerButtonPressed(nativeButton) : seat->pointerButtonReleased(nativeButton);
}
}
return true;
@ -374,13 +374,10 @@ public:
return false;
}
auto seat = waylandServer()->seat();
if (KWayland::Server::SurfaceInterface *s = seat->focusedPointerSurface()) {
Toplevel *t = waylandServer()->findClient(s);
if (t->isLockScreen() || t->isInputMethod()) {
seat->setTimestamp(event->timestamp());
const Qt::Orientation orientation = event->angleDelta().x() == 0 ? Qt::Vertical : Qt::Horizontal;
seat->pointerAxis(orientation, orientation == Qt::Horizontal ? event->angleDelta().x() : event->angleDelta().y());
}
if (pointerSurfaceAllowed()) {
seat->setTimestamp(event->timestamp());
const Qt::Orientation orientation = event->angleDelta().x() == 0 ? Qt::Vertical : Qt::Horizontal;
seat->pointerAxis(orientation, orientation == Qt::Horizontal ? event->angleDelta().x() : event->angleDelta().y());
}
return true;
}
@ -425,6 +422,16 @@ public:
}
return true;
}
private:
bool pointerSurfaceAllowed() const {
if (KWayland::Server::SurfaceInterface *s = waylandServer()->seat()->focusedPointerSurface()) {
if (Toplevel *t = waylandServer()->findClient(s)) {
return t->isLockScreen() || t->isInputMethod();
}
return false;
}
return true;
}
};
class EffectsFilter : public InputEventFilter {
@ -878,6 +885,8 @@ void InputRedirection::setupWorkspace()
}
);
connect(workspace(), &Workspace::configChanged, this, &InputRedirection::reconfigure);
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &InputRedirection::updatePointerWindow);
}
setupInputFilters();
}
@ -1034,9 +1043,6 @@ void InputRedirection::setupLibInputWithScreens()
void InputRedirection::updatePointerWindow()
{
if (waylandServer() && waylandServer()->isScreenLocked()) {
return;
}
// TODO: handle pointer grab aka popups
Toplevel *t = findToplevel(m_globalPointer.toPoint());
updatePointerInternalWindow();
@ -1090,6 +1096,7 @@ void InputRedirection::updatePointerWindow()
void InputRedirection::updatePointerDecoration(Toplevel *t)
{
const auto oldDeco = m_pointerDecoration;
bool needsReset = waylandServer() && waylandServer()->isScreenLocked();
if (AbstractClient *c = dynamic_cast<AbstractClient*>(t)) {
// check whether it's on a Decoration
if (c->decoratedClient()) {
@ -1097,12 +1104,15 @@ void InputRedirection::updatePointerDecoration(Toplevel *t)
if (!clientRect.contains(m_globalPointer.toPoint())) {
m_pointerDecoration = c->decoratedClient();
} else {
m_pointerDecoration.clear();
needsReset = true;
}
} else {
m_pointerDecoration.clear();
needsReset = true;
}
} else {
needsReset = true;
}
if (needsReset) {
m_pointerDecoration.clear();
}
@ -1128,6 +1138,7 @@ void InputRedirection::updatePointerInternalWindow()
const auto oldInternalWindow = m_pointerInternalWindow;
if (waylandServer()) {
bool found = false;
bool needsReset = waylandServer()->isScreenLocked();
const auto &internalClients = waylandServer()->internalClients();
const bool change = m_pointerInternalWindow.isNull() || !(m_pointerInternalWindow->flags().testFlag(Qt::Popup) && m_pointerInternalWindow->isVisible());
if (!internalClients.isEmpty() && change) {
@ -1146,9 +1157,12 @@ void InputRedirection::updatePointerInternalWindow()
}
} while (it != internalClients.begin());
if (!found) {
m_pointerInternalWindow.clear();
needsReset = true;
}
}
if (needsReset) {
m_pointerInternalWindow.clear();
}
}
if (oldInternalWindow != m_pointerInternalWindow) {
// changed