Use locked cursor position hint

Summary:
Listen for cursor position hint changes and set cursor position to the hint
on unlock if a valid position hint was set.

Test Plan: With pointer constraints test app.

Reviewers: #kwin, graesslin

Reviewed By: #kwin, graesslin

Subscribers: graesslin, kwin

Tags: #kwin

Maniphest Tasks: T4693

Differential Revision: https://phabricator.kde.org/D14176
icc-effect-5.14.5
Roman Gilg 2018-07-18 09:06:56 +02:00
parent d723ce2c40
commit 054ccc3898
5 changed files with 76 additions and 0 deletions

View File

@ -594,6 +594,12 @@ void PointerInputRedirection::disconnectConfinedPointerRegionConnection()
m_confinedPointerRegionConnection = QMetaObject::Connection();
}
void PointerInputRedirection::disconnectLockedPointerAboutToBeUnboundConnection()
{
disconnect(m_lockedPointerAboutToBeUnboundConnection);
m_lockedPointerAboutToBeUnboundConnection = QMetaObject::Connection();
}
void PointerInputRedirection::disconnectPointerConstraintsConnection()
{
disconnect(m_constraintsConnection);
@ -689,8 +695,13 @@ void PointerInputRedirection::updatePointerConstraints()
if (lock) {
if (lock->isLocked()) {
if (!canConstrain) {
const auto hint = lock->cursorPositionHint();
lock->setLocked(false);
m_locked = false;
disconnectLockedPointerAboutToBeUnboundConnection();
if (! (hint.x() < 0 || hint.y() < 0) && m_window) {
processMotion(m_window->pos() - m_window->clientContentPos() + hint, waylandServer()->seat()->timestamp());
}
}
return;
}
@ -698,6 +709,24 @@ void PointerInputRedirection::updatePointerConstraints()
if (canConstrain && r.contains(m_pos.toPoint())) {
lock->setLocked(true);
m_locked = true;
// The client might cancel pointer locking from its side by unbinding the LockedPointerInterface.
// In this case the cached cursor position hint must be fetched before the resource goes away
m_lockedPointerAboutToBeUnboundConnection = connect(lock.data(), &KWayland::Server::LockedPointerInterface::aboutToBeUnbound, this,
[this, lock]() {
const auto hint = lock->cursorPositionHint();
if (hint.x() < 0 || hint.y() < 0 || !m_window) {
return;
}
auto globalHint = m_window->pos() - m_window->clientContentPos() + hint;
// When the resource finally goes away, reposition the cursor according to the hint
connect(lock.data(), &KWayland::Server::LockedPointerInterface::unbound, this,
[this, globalHint]() {
processMotion(globalHint, waylandServer()->seat()->timestamp());
});
}
);
OSD::show(i18nc("notification about mouse pointer locked",
"Pointer locked to current position.\nTo end pointer lock hold Escape for 3 seconds."),
QStringLiteral("preferences-desktop-mouse"), 5000);
@ -705,6 +734,7 @@ void PointerInputRedirection::updatePointerConstraints()
}
} else {
m_locked = false;
disconnectLockedPointerAboutToBeUnboundConnection();
}
}

View File

@ -155,6 +155,7 @@ private:
void warpXcbOnSurfaceLeft(KWayland::Server::SurfaceInterface *surface);
QPointF applyPointerConfinement(const QPointF &pos) const;
void disconnectConfinedPointerRegionConnection();
void disconnectLockedPointerAboutToBeUnboundConnection();
void disconnectPointerConstraintsConnection();
void breakPointerConstraints(KWayland::Server::SurfaceInterface *surface);
bool areButtonsPressed() const;
@ -169,6 +170,7 @@ private:
QMetaObject::Connection m_constraintsConnection;
QMetaObject::Connection m_constraintsActivatedConnection;
QMetaObject::Connection m_confinedPointerRegionConnection;
QMetaObject::Connection m_lockedPointerAboutToBeUnboundConnection;
QMetaObject::Connection m_decorationGeometryConnection;
bool m_confined = false;
bool m_locked = false;

View File

@ -142,8 +142,14 @@ void WaylandBackend::lockRequest(bool persistent, QRect region)
}
m_lockedPointer = lockedPointer;
m_lockedPointerPersistent = persistent;
connect(lockedPointer, &LockedPointer::locked, this, [this]() {
qDebug() << "------ LOCKED! ------";
if(lockHint()) {
m_lockedPointer->setCursorPositionHint(QPointF(10., 10.));
forceSurfaceCommit();
}
Q_EMIT lockChanged(true);
});
connect(lockedPointer, &LockedPointer::unlocked, this, [this]() {

View File

@ -49,6 +49,7 @@ public:
Backend(QObject *parent = nullptr) : QObject(parent) {}
Q_PROPERTY(int mode READ mode CONSTANT)
Q_PROPERTY(bool lockHint MEMBER m_lockHint NOTIFY lockHintChanged)
Q_PROPERTY(bool errorsAllowed READ errorsAllowed WRITE setErrorsAllowed NOTIFY errorsAllowedChanged)
virtual void init(QQuickView *view) {
@ -57,6 +58,10 @@ public:
int mode() const {
return (int)m_mode;
}
bool lockHint() const {
return m_lockHint;
}
bool errorsAllowed() const {
return m_errorsAllowed;
}
@ -87,7 +92,9 @@ public:
Q_SIGNALS:
void confineChanged(bool confined);
void lockChanged(bool locked);
void lockHintChanged();
void errorsAllowedChanged();
void forceSurfaceCommit();
protected:
enum class Mode {
@ -105,6 +112,8 @@ protected:
private:
QQuickView *m_view;
Mode m_mode;
bool m_lockHint = true;
bool m_errorsAllowed = false;
};

View File

@ -79,6 +79,28 @@ ColumnLayout {
return activArea.rect();
}
Connections {
target: org_kde_kwin_tests_pointerconstraints_backend
onForceSurfaceCommit: {
forceCommitRect.visible = true
}
}
Rectangle {
id: forceCommitRect
width: 10
height: 10
color: "red"
visible: false
Timer {
interval: 500
running: forceCommitRect.visible
repeat: false
onTriggered: forceCommitRect.visible = false;
}
}
GridLayout {
columns: 2
rowSpacing: 10
@ -120,6 +142,13 @@ ColumnLayout {
}
}
CheckBox {
id: lockHintChck
text: "Send position hint on lock"
checked: root.waylandNative
enabled: root.waylandNative
onCheckedChanged: org_kde_kwin_tests_pointerconstraints_backend.lockHint = checked;
}
CheckBox {
id: restrAreaChck
text: "Restrict input area (not yet implemented)"