wayland: Fix null pointer constraint region handling
If the pointer constraint region is null, the input region must be used instead. If the pointer constraint region is valid, it should be intersected with the input region. BUG: 457021
This commit is contained in:
parent
825abf1ca3
commit
6c71b87200
7 changed files with 86 additions and 57 deletions
|
@ -597,14 +597,6 @@ void PointerInputRedirection::disconnectPointerConstraintsConnection()
|
|||
m_constraintsActivatedConnection = QMetaObject::Connection();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static QRegion getConstraintRegion(Window *window, T *constraint)
|
||||
{
|
||||
const QRegion windowShape = window->inputShape();
|
||||
const QRegion intersected = constraint->region().isEmpty() ? windowShape : windowShape.intersected(constraint->region());
|
||||
return intersected.translated(window->mapFromLocal(QPointF(0, 0)).toPoint());
|
||||
}
|
||||
|
||||
void PointerInputRedirection::setEnableConstraints(bool set)
|
||||
{
|
||||
if (m_enableConstraints == set) {
|
||||
|
@ -640,8 +632,7 @@ void PointerInputRedirection::updatePointerConstraints()
|
|||
}
|
||||
return;
|
||||
}
|
||||
const QRegion r = getConstraintRegion(focus(), cf);
|
||||
if (canConstrain && r.contains(m_pos.toPoint())) {
|
||||
if (canConstrain && cf->region().contains(focus()->mapToLocal(m_pos).toPoint())) {
|
||||
cf->setConfined(true);
|
||||
m_confined = true;
|
||||
m_confinedPointerRegionConnection = connect(cf, &KWaylandServer::ConfinedPointerV1Interface::regionChanged, this, [this]() {
|
||||
|
@ -653,7 +644,7 @@ void PointerInputRedirection::updatePointerConstraints()
|
|||
return;
|
||||
}
|
||||
const auto cf = s->confinedPointer();
|
||||
if (!getConstraintRegion(focus(), cf).contains(m_pos.toPoint())) {
|
||||
if (!cf->region().contains(focus()->mapToLocal(m_pos).toPoint())) {
|
||||
// pointer no longer in confined region, break the confinement
|
||||
cf->setConfined(false);
|
||||
m_confined = false;
|
||||
|
@ -684,8 +675,7 @@ void PointerInputRedirection::updatePointerConstraints()
|
|||
}
|
||||
return;
|
||||
}
|
||||
const QRegion r = getConstraintRegion(focus(), lock);
|
||||
if (canConstrain && r.contains(m_pos.toPoint())) {
|
||||
if (canConstrain && lock->region().contains(focus()->mapToLocal(m_pos).toPoint())) {
|
||||
lock->setLocked(true);
|
||||
m_locked = true;
|
||||
|
||||
|
@ -728,20 +718,22 @@ QPointF PointerInputRedirection::applyPointerConfinement(const QPointF &pos) con
|
|||
return pos;
|
||||
}
|
||||
|
||||
const QRegion confinementRegion = getConstraintRegion(focus(), cf);
|
||||
if (confinementRegion.contains(flooredPoint(pos))) {
|
||||
const QPointF localPos = focus()->mapToLocal(pos);
|
||||
if (cf->region().contains(flooredPoint(localPos))) {
|
||||
return pos;
|
||||
}
|
||||
QPointF p = pos;
|
||||
// allow either x or y to pass
|
||||
p = QPointF(m_pos.x(), pos.y());
|
||||
|
||||
if (confinementRegion.contains(flooredPoint(p))) {
|
||||
return p;
|
||||
const QPointF currentPos = focus()->mapToLocal(m_pos);
|
||||
|
||||
// allow either x or y to pass
|
||||
QPointF p(currentPos.x(), localPos.y());
|
||||
if (cf->region().contains(flooredPoint(p))) {
|
||||
return focus()->mapFromLocal(p);
|
||||
}
|
||||
p = QPointF(pos.x(), m_pos.y());
|
||||
if (confinementRegion.contains(flooredPoint(p))) {
|
||||
return p;
|
||||
|
||||
p = QPointF(localPos.x(), currentPos.y());
|
||||
if (cf->region().contains(flooredPoint(p))) {
|
||||
return focus()->mapFromLocal(p);
|
||||
}
|
||||
|
||||
return m_pos;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "KWayland/Client/pointerconstraints.h"
|
||||
#include "KWayland/Client/registry.h"
|
||||
#include "KWayland/Client/seat.h"
|
||||
#include "KWayland/Client/shm_pool.h"
|
||||
#include "KWayland/Client/surface.h"
|
||||
// server
|
||||
#include "wayland/compositor_interface.h"
|
||||
|
@ -52,6 +53,7 @@ private:
|
|||
KWayland::Client::EventQueue *m_queue = nullptr;
|
||||
KWayland::Client::Compositor *m_compositor = nullptr;
|
||||
KWayland::Client::Seat *m_seat = nullptr;
|
||||
KWayland::Client::ShmPool *m_shm = nullptr;
|
||||
KWayland::Client::Pointer *m_pointer = nullptr;
|
||||
KWayland::Client::PointerConstraints *m_pointerConstraints = nullptr;
|
||||
};
|
||||
|
@ -95,6 +97,11 @@ void TestPointerConstraints::init()
|
|||
registry.setup();
|
||||
QVERIFY(interfacesAnnouncedSpy.wait());
|
||||
|
||||
m_shm = new KWayland::Client::ShmPool(this);
|
||||
m_shm->setup(registry.bindShm(registry.interface(KWayland::Client::Registry::Interface::Shm).name,
|
||||
registry.interface(KWayland::Client::Registry::Interface::Shm).version));
|
||||
QVERIFY(m_shm->isValid());
|
||||
|
||||
m_compositor =
|
||||
registry.createCompositor(registry.interface(KWayland::Client::Registry::Interface::Compositor).name, registry.interface(KWayland::Client::Registry::Interface::Compositor).version, this);
|
||||
QVERIFY(m_compositor);
|
||||
|
@ -125,6 +132,7 @@ void TestPointerConstraints::cleanup()
|
|||
CLEANUP(m_compositor)
|
||||
CLEANUP(m_pointerConstraints)
|
||||
CLEANUP(m_pointer)
|
||||
CLEANUP(m_shm)
|
||||
CLEANUP(m_seat)
|
||||
CLEANUP(m_queue)
|
||||
if (m_connection) {
|
||||
|
@ -167,6 +175,12 @@ void TestPointerConstraints::testLockPointer()
|
|||
QVERIFY(surface->isValid());
|
||||
QVERIFY(surfaceCreatedSpy.wait());
|
||||
|
||||
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::black);
|
||||
surface->attachBuffer(m_shm->createBuffer(image));
|
||||
surface->damage(image.rect());
|
||||
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
|
||||
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
||||
QVERIFY(serverSurface);
|
||||
QVERIFY(!serverSurface->lockedPointer());
|
||||
|
@ -186,7 +200,7 @@ void TestPointerConstraints::testLockPointer()
|
|||
QVERIFY(!serverSurface->confinedPointer());
|
||||
|
||||
QCOMPARE(serverLockedPointer->isLocked(), false);
|
||||
QCOMPARE(serverLockedPointer->region(), QRegion());
|
||||
QCOMPARE(serverLockedPointer->region(), QRegion(0, 0, 100, 100));
|
||||
QFETCH(LockedPointerV1Interface::LifeTime, serverLifeTime);
|
||||
QCOMPARE(serverLockedPointer->lifeTime(), serverLifeTime);
|
||||
// setting to unlocked now should not trigger an unlocked spy
|
||||
|
@ -206,7 +220,7 @@ void TestPointerConstraints::testLockPointer()
|
|||
lockedPointer->setRegion(nullptr);
|
||||
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
QVERIFY(regionChangedSpy.wait());
|
||||
QCOMPARE(serverLockedPointer->region(), QRegion());
|
||||
QCOMPARE(serverLockedPointer->region(), QRegion(0, 0, 100, 100));
|
||||
|
||||
// let's lock the surface
|
||||
QSignalSpy lockedChangedSpy(serverLockedPointer, &LockedPointerV1Interface::lockedChanged);
|
||||
|
@ -277,6 +291,12 @@ void TestPointerConstraints::testConfinePointer()
|
|||
QVERIFY(surface->isValid());
|
||||
QVERIFY(surfaceCreatedSpy.wait());
|
||||
|
||||
QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::black);
|
||||
surface->attachBuffer(m_shm->createBuffer(image));
|
||||
surface->damage(image.rect());
|
||||
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
|
||||
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
|
||||
QVERIFY(serverSurface);
|
||||
QVERIFY(!serverSurface->lockedPointer());
|
||||
|
@ -296,7 +316,7 @@ void TestPointerConstraints::testConfinePointer()
|
|||
QVERIFY(!serverSurface->lockedPointer());
|
||||
|
||||
QCOMPARE(serverConfinedPointer->isConfined(), false);
|
||||
QCOMPARE(serverConfinedPointer->region(), QRegion());
|
||||
QCOMPARE(serverConfinedPointer->region(), QRegion(0, 0, 100, 100));
|
||||
QFETCH(ConfinedPointerV1Interface::LifeTime, serverLifeTime);
|
||||
QCOMPARE(serverConfinedPointer->lifeTime(), serverLifeTime);
|
||||
// setting to unconfined now should not trigger an unconfined spy
|
||||
|
@ -316,7 +336,7 @@ void TestPointerConstraints::testConfinePointer()
|
|||
confinedPointer->setRegion(nullptr);
|
||||
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
QVERIFY(regionChangedSpy.wait());
|
||||
QCOMPARE(serverConfinedPointer->region(), QRegion());
|
||||
QCOMPARE(serverConfinedPointer->region(), QRegion(0, 0, 100, 100));
|
||||
|
||||
// let's confine the surface
|
||||
QSignalSpy confinedChangedSpy(serverConfinedPointer, &ConfinedPointerV1Interface::confinedChanged);
|
||||
|
|
|
@ -141,21 +141,35 @@ LockedPointerV1InterfacePrivate::LockedPointerV1InterfacePrivate(LockedPointerV1
|
|||
, q(q)
|
||||
, surface(surface)
|
||||
, lifeTime(lifeTime)
|
||||
, region(region)
|
||||
, pendingRegion(region)
|
||||
, hasPendingRegion(true)
|
||||
{
|
||||
commit();
|
||||
}
|
||||
|
||||
void LockedPointerV1InterfacePrivate::commit()
|
||||
{
|
||||
qreal scaleOverride = surface->scaleOverride();
|
||||
const QRegion oldRegion = effectiveRegion;
|
||||
const QPointF oldHint = hint;
|
||||
|
||||
if (hasPendingRegion) {
|
||||
region = mapScaleOverride(pendingRegion, scaleOverride);
|
||||
region = mapScaleOverride(pendingRegion, surface->scaleOverride());
|
||||
hasPendingRegion = false;
|
||||
Q_EMIT q->regionChanged();
|
||||
}
|
||||
if (hasPendingHint) {
|
||||
hint = pendingHint / scaleOverride;
|
||||
hint = pendingHint / surface->scaleOverride();
|
||||
hasPendingHint = false;
|
||||
}
|
||||
|
||||
effectiveRegion = surface->input();
|
||||
if (!region.isEmpty()) {
|
||||
effectiveRegion &= region;
|
||||
}
|
||||
|
||||
if (oldRegion != effectiveRegion) {
|
||||
Q_EMIT q->regionChanged();
|
||||
}
|
||||
if (oldHint != hint) {
|
||||
Q_EMIT q->cursorPositionHintChanged();
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +214,7 @@ LockedPointerV1Interface::LifeTime LockedPointerV1Interface::lifeTime() const
|
|||
|
||||
QRegion LockedPointerV1Interface::region() const
|
||||
{
|
||||
return d->region;
|
||||
return d->effectiveRegion;
|
||||
}
|
||||
|
||||
QPointF LockedPointerV1Interface::cursorPositionHint() const
|
||||
|
@ -244,16 +258,27 @@ ConfinedPointerV1InterfacePrivate::ConfinedPointerV1InterfacePrivate(ConfinedPoi
|
|||
, q(q)
|
||||
, surface(surface)
|
||||
, lifeTime(lifeTime)
|
||||
, region(region)
|
||||
, pendingRegion(region)
|
||||
, hasPendingRegion(true)
|
||||
{
|
||||
commit();
|
||||
}
|
||||
|
||||
void ConfinedPointerV1InterfacePrivate::commit()
|
||||
{
|
||||
qreal scaleOverride = surface->scaleOverride();
|
||||
const QRegion oldRegion = effectiveRegion;
|
||||
|
||||
if (hasPendingRegion) {
|
||||
region = mapScaleOverride(pendingRegion, scaleOverride);
|
||||
region = mapScaleOverride(pendingRegion, surface->scaleOverride());
|
||||
hasPendingRegion = false;
|
||||
}
|
||||
|
||||
effectiveRegion = surface->input();
|
||||
if (!region.isEmpty()) {
|
||||
effectiveRegion &= region;
|
||||
}
|
||||
|
||||
if (oldRegion != effectiveRegion) {
|
||||
Q_EMIT q->regionChanged();
|
||||
}
|
||||
}
|
||||
|
@ -291,7 +316,7 @@ ConfinedPointerV1Interface::LifeTime ConfinedPointerV1Interface::lifeTime() cons
|
|||
|
||||
QRegion ConfinedPointerV1Interface::region() const
|
||||
{
|
||||
return d->region;
|
||||
return d->effectiveRegion;
|
||||
}
|
||||
|
||||
bool ConfinedPointerV1Interface::isConfined() const
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
LockedPointerV1Interface *q;
|
||||
QPointer<SurfaceInterface> surface;
|
||||
LockedPointerV1Interface::LifeTime lifeTime;
|
||||
QRegion effectiveRegion;
|
||||
QRegion region;
|
||||
QRegion pendingRegion;
|
||||
QPointF hint = QPointF(-1, -1);
|
||||
|
@ -77,6 +78,7 @@ public:
|
|||
ConfinedPointerV1Interface *q;
|
||||
QPointer<SurfaceInterface> surface;
|
||||
ConfinedPointerV1Interface::LifeTime lifeTime;
|
||||
QRegion effectiveRegion;
|
||||
QRegion region;
|
||||
QRegion pendingRegion;
|
||||
bool hasPendingRegion = false;
|
||||
|
|
|
@ -572,15 +572,6 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
|
|||
next->mergeInto(¤t);
|
||||
scaleOverride = pendingScaleOverride;
|
||||
|
||||
if (lockedPointer) {
|
||||
auto lockedPointerPrivate = LockedPointerV1InterfacePrivate::get(lockedPointer);
|
||||
lockedPointerPrivate->commit();
|
||||
}
|
||||
if (confinedPointer) {
|
||||
auto confinedPointerPrivate = ConfinedPointerV1InterfacePrivate::get(confinedPointer);
|
||||
confinedPointerPrivate->commit();
|
||||
}
|
||||
|
||||
if (bufferRef != current.buffer) {
|
||||
if (bufferRef) {
|
||||
bufferRef->unref();
|
||||
|
@ -646,6 +637,16 @@ void SurfaceInterfacePrivate::applyState(SurfaceState *next)
|
|||
|
||||
surfaceToBufferMatrix = buildSurfaceToBufferMatrix();
|
||||
bufferToSurfaceMatrix = surfaceToBufferMatrix.inverted();
|
||||
|
||||
if (lockedPointer) {
|
||||
auto lockedPointerPrivate = LockedPointerV1InterfacePrivate::get(lockedPointer);
|
||||
lockedPointerPrivate->commit();
|
||||
}
|
||||
if (confinedPointer) {
|
||||
auto confinedPointerPrivate = ConfinedPointerV1InterfacePrivate::get(confinedPointer);
|
||||
confinedPointerPrivate->commit();
|
||||
}
|
||||
|
||||
if (opaqueRegionChanged) {
|
||||
Q_EMIT q->opaqueChanged(opaqueRegion);
|
||||
}
|
||||
|
|
|
@ -369,16 +369,6 @@ QString Window::windowRole() const
|
|||
return QString();
|
||||
}
|
||||
|
||||
QRegion Window::inputShape() const
|
||||
{
|
||||
if (m_surface) {
|
||||
return m_surface->input();
|
||||
} else {
|
||||
// TODO: maybe also for X11?
|
||||
return QRegion();
|
||||
}
|
||||
}
|
||||
|
||||
QMatrix4x4 Window::inputTransformation() const
|
||||
{
|
||||
QMatrix4x4 m;
|
||||
|
|
|
@ -754,7 +754,6 @@ public:
|
|||
virtual pid_t pid() const;
|
||||
|
||||
bool readyForPainting() const; // true if the window has been already painted its contents
|
||||
QRegion inputShape() const;
|
||||
void setOpacity(qreal opacity);
|
||||
qreal opacity() const;
|
||||
virtual bool setupCompositing();
|
||||
|
|
Loading…
Reference in a new issue