Implementation of PointerConstraints protcol
Summary: The pointer constraints protocol is an unstable protocol and thus the implementation follows the semantics of unstable protocols. The protocol allows to create a constraint on the pointer - either a lock or a confinement on a surface. Those are not activated at once, but when the compositor actively grants it. During lock no further pointer motion is emitted, during confinement the pointer is kept in a certain area. This implements T4451. Reviewers: #plasma_on_wayland Subscribers: plasma-devel Tags: #plasma_on_wayland Differential Revision: https://phabricator.kde.org/D3466
This commit is contained in:
parent
c72510c932
commit
b44a8fb556
15 changed files with 1517 additions and 0 deletions
|
@ -21,6 +21,8 @@ set(SERVER_LIB_SRCS
|
|||
pointer_interface.cpp
|
||||
plasmashell_interface.cpp
|
||||
plasmawindowmanagement_interface.cpp
|
||||
pointerconstraints_interface.cpp
|
||||
pointerconstraints_interface_v1.cpp
|
||||
pointergestures_interface.cpp
|
||||
pointergestures_interface_v1.cpp
|
||||
qtsurfaceextension_interface.cpp
|
||||
|
@ -134,6 +136,11 @@ ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
|
|||
BASENAME pointer-gestures-unstable-v1
|
||||
)
|
||||
|
||||
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
|
||||
PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/pointer-constraints-unstable-v1.xml
|
||||
BASENAME pointer-constraints-unstable-v1
|
||||
)
|
||||
|
||||
add_library(KF5WaylandServer ${SERVER_LIB_SRCS})
|
||||
generate_export_header(KF5WaylandServer
|
||||
BASE_NAME
|
||||
|
@ -185,6 +192,7 @@ install(FILES
|
|||
outputmanagement_interface.h
|
||||
output_interface.h
|
||||
pointer_interface.h
|
||||
pointerconstraints_interface.h
|
||||
pointergestures_interface.h
|
||||
plasmashell_interface.h
|
||||
plasmawindowmanagement_interface.h
|
||||
|
|
|
@ -348,3 +348,10 @@ target_link_libraries( testXdgShellV5 Qt5::Test Qt5::Gui KF5::WaylandServer KF5:
|
|||
add_test(kwayland-testXdgShellV5 testXdgShellV5)
|
||||
ecm_mark_as_test(testXdgShellV5)
|
||||
|
||||
########################################################
|
||||
# Test Pointer Constraints
|
||||
########################################################
|
||||
add_executable(testPointerConstraints test_pointer_constraints.cpp)
|
||||
target_link_libraries( testPointerConstraints Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client)
|
||||
add_test(kwayland-testPointerConstraints testPointerConstraints)
|
||||
ecm_mark_as_test(testPointerConstraints)
|
||||
|
|
433
src/wayland/autotests/client/test_pointer_constraints.cpp
Normal file
433
src/wayland/autotests/client/test_pointer_constraints.cpp
Normal file
|
@ -0,0 +1,433 @@
|
|||
/********************************************************************
|
||||
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) version 3, or any
|
||||
later version accepted by the membership of KDE e.V. (or its
|
||||
successor approved by the membership of KDE e.V.), which shall
|
||||
act as a proxy defined in Section 6 of version 3 of the license.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
// Qt
|
||||
#include <QtTest/QtTest>
|
||||
// client
|
||||
#include "../../src/client/connection_thread.h"
|
||||
#include "../../src/client/compositor.h"
|
||||
#include "../../src/client/event_queue.h"
|
||||
#include "../../src/client/pointer.h"
|
||||
#include "../../src/client/pointerconstraints.h"
|
||||
#include "../../src/client/registry.h"
|
||||
#include "../../src/client/seat.h"
|
||||
#include "../../src/client/surface.h"
|
||||
// server
|
||||
#include "../../src/server/display.h"
|
||||
#include "../../src/server/compositor_interface.h"
|
||||
#include "../../src/server/pointerconstraints_interface.h"
|
||||
#include "../../src/server/seat_interface.h"
|
||||
#include "../../src/server/surface_interface.h"
|
||||
|
||||
using namespace KWayland::Client;
|
||||
using namespace KWayland::Server;
|
||||
|
||||
Q_DECLARE_METATYPE(KWayland::Client::PointerConstraints::LifeTime)
|
||||
Q_DECLARE_METATYPE(KWayland::Server::ConfinedPointerInterface::LifeTime)
|
||||
Q_DECLARE_METATYPE(KWayland::Server::LockedPointerInterface::LifeTime)
|
||||
|
||||
class TestPointerConstraints : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testLockPointer_data();
|
||||
void testLockPointer();
|
||||
|
||||
void testConfinePointer_data();
|
||||
void testConfinePointer();
|
||||
void testAlreadyConstrained_data();
|
||||
void testAlreadyConstrained();
|
||||
|
||||
private:
|
||||
Display *m_display = nullptr;
|
||||
CompositorInterface *m_compositorInterface = nullptr;
|
||||
SeatInterface *m_seatInterface = nullptr;
|
||||
PointerConstraintsInterface *m_pointerConstraintsInterface = nullptr;
|
||||
ConnectionThread *m_connection = nullptr;
|
||||
QThread *m_thread = nullptr;
|
||||
EventQueue *m_queue = nullptr;
|
||||
Compositor *m_compositor = nullptr;
|
||||
Seat *m_seat = nullptr;
|
||||
Pointer *m_pointer = nullptr;
|
||||
PointerConstraints *m_pointerConstraints = nullptr;
|
||||
};
|
||||
|
||||
static const QString s_socketName = QStringLiteral("kwayland-test-pointer_constraint-0");
|
||||
|
||||
void TestPointerConstraints::init()
|
||||
{
|
||||
delete m_display;
|
||||
m_display = new Display(this);
|
||||
m_display->setSocketName(s_socketName);
|
||||
m_display->start();
|
||||
QVERIFY(m_display->isRunning());
|
||||
m_display->createShm();
|
||||
m_seatInterface = m_display->createSeat(m_display);
|
||||
m_seatInterface->setHasPointer(true);
|
||||
m_seatInterface->create();
|
||||
m_compositorInterface = m_display->createCompositor(m_display);
|
||||
m_compositorInterface->create();
|
||||
m_pointerConstraintsInterface = m_display->createPointerConstraints(PointerConstraintsInterfaceVersion::UnstableV1, m_display);
|
||||
m_pointerConstraintsInterface->create();
|
||||
|
||||
// setup connection
|
||||
m_connection = new KWayland::Client::ConnectionThread;
|
||||
QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected);
|
||||
QVERIFY(connectedSpy.isValid());
|
||||
m_connection->setSocketName(s_socketName);
|
||||
|
||||
m_thread = new QThread(this);
|
||||
m_connection->moveToThread(m_thread);
|
||||
m_thread->start();
|
||||
|
||||
m_connection->initConnection();
|
||||
QVERIFY(connectedSpy.wait());
|
||||
|
||||
m_queue = new EventQueue(this);
|
||||
m_queue->setup(m_connection);
|
||||
|
||||
Registry registry;
|
||||
QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced);
|
||||
QVERIFY(interfacesAnnouncedSpy.isValid());
|
||||
QSignalSpy interfaceAnnouncedSpy(®istry, &Registry::interfaceAnnounced);
|
||||
QVERIFY(interfaceAnnouncedSpy.isValid());
|
||||
registry.setEventQueue(m_queue);
|
||||
registry.create(m_connection);
|
||||
QVERIFY(registry.isValid());
|
||||
registry.setup();
|
||||
QVERIFY(interfacesAnnouncedSpy.wait());
|
||||
|
||||
m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this);
|
||||
QVERIFY(m_compositor);
|
||||
QVERIFY(m_compositor->isValid());
|
||||
|
||||
m_pointerConstraints = registry.createPointerConstraints(registry.interface(Registry::Interface::PointerConstraintsUnstableV1).name,
|
||||
registry.interface(Registry::Interface::PointerConstraintsUnstableV1).version, this);
|
||||
QVERIFY(m_pointerConstraints);
|
||||
QVERIFY(m_pointerConstraints->isValid());
|
||||
|
||||
m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this);
|
||||
QVERIFY(m_seat);
|
||||
QVERIFY(m_seat->isValid());
|
||||
QSignalSpy pointerChangedSpy(m_seat, &Seat::hasPointerChanged);
|
||||
QVERIFY(pointerChangedSpy.isValid());
|
||||
QVERIFY(pointerChangedSpy.wait());
|
||||
m_pointer = m_seat->createPointer(this);
|
||||
QVERIFY(m_pointer);
|
||||
}
|
||||
|
||||
void TestPointerConstraints::cleanup()
|
||||
{
|
||||
#define CLEANUP(variable) \
|
||||
if (variable) { \
|
||||
delete variable; \
|
||||
variable = nullptr; \
|
||||
}
|
||||
CLEANUP(m_compositor)
|
||||
CLEANUP(m_pointerConstraints)
|
||||
CLEANUP(m_pointer)
|
||||
CLEANUP(m_seat)
|
||||
CLEANUP(m_queue)
|
||||
if (m_connection) {
|
||||
m_connection->deleteLater();
|
||||
m_connection = nullptr;
|
||||
}
|
||||
if (m_thread) {
|
||||
m_thread->quit();
|
||||
m_thread->wait();
|
||||
delete m_thread;
|
||||
m_thread = nullptr;
|
||||
}
|
||||
|
||||
CLEANUP(m_compositorInterface)
|
||||
CLEANUP(m_seatInterface);
|
||||
CLEANUP(m_pointerConstraintsInterface)
|
||||
CLEANUP(m_display)
|
||||
#undef CLEANUP
|
||||
}
|
||||
|
||||
void TestPointerConstraints::testLockPointer_data()
|
||||
{
|
||||
QTest::addColumn<PointerConstraints::LifeTime>("clientLifeTime");
|
||||
QTest::addColumn<LockedPointerInterface::LifeTime>("serverLifeTime");
|
||||
QTest::addColumn<bool>("hasConstraintAfterUnlock");
|
||||
QTest::addColumn<int>("pointerChangedCount");
|
||||
|
||||
QTest::newRow("persistent") << PointerConstraints::LifeTime::Persistent << LockedPointerInterface::LifeTime::Persistent << true << 1;
|
||||
QTest::newRow("oneshot") << PointerConstraints::LifeTime::OneShot << LockedPointerInterface::LifeTime::OneShot << false << 2;
|
||||
}
|
||||
|
||||
void TestPointerConstraints::testLockPointer()
|
||||
{
|
||||
// this test verifies the basic interaction for lock pointer
|
||||
// first create a surface
|
||||
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
||||
QVERIFY(surfaceCreatedSpy.isValid());
|
||||
QScopedPointer<Surface> surface(m_compositor->createSurface());
|
||||
QVERIFY(surface->isValid());
|
||||
QVERIFY(surfaceCreatedSpy.wait());
|
||||
|
||||
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
|
||||
QVERIFY(serverSurface);
|
||||
QVERIFY(serverSurface->lockedPointer().isNull());
|
||||
QVERIFY(serverSurface->confinedPointer().isNull());
|
||||
|
||||
// now create the locked pointer
|
||||
QSignalSpy pointerConstraintsChangedSpy(serverSurface, &SurfaceInterface::pointerConstraintsChanged);
|
||||
QVERIFY(pointerConstraintsChangedSpy.isValid());
|
||||
QFETCH(PointerConstraints::LifeTime, clientLifeTime);
|
||||
QScopedPointer<LockedPointer> lockedPointer(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, clientLifeTime));
|
||||
QSignalSpy lockedSpy(lockedPointer.data(), &LockedPointer::locked);
|
||||
QVERIFY(lockedSpy.isValid());
|
||||
QSignalSpy unlockedSpy(lockedPointer.data(), &LockedPointer::unlocked);
|
||||
QVERIFY(unlockedSpy.isValid());
|
||||
QVERIFY(lockedPointer->isValid());
|
||||
QVERIFY(pointerConstraintsChangedSpy.wait());
|
||||
|
||||
auto serverLockedPointer = serverSurface->lockedPointer();
|
||||
QVERIFY(serverLockedPointer);
|
||||
QVERIFY(serverSurface->confinedPointer().isNull());
|
||||
|
||||
QCOMPARE(serverLockedPointer->isLocked(), false);
|
||||
QCOMPARE(serverLockedPointer->region(), QRegion());
|
||||
QFETCH(LockedPointerInterface::LifeTime, serverLifeTime);
|
||||
QCOMPARE(serverLockedPointer->lifeTime(), serverLifeTime);
|
||||
// setting to unlocked now should not trigger an unlocked spy
|
||||
serverLockedPointer->setLocked(false);
|
||||
QVERIFY(!unlockedSpy.wait());
|
||||
|
||||
// try setting a region
|
||||
QSignalSpy destroyedSpy(serverLockedPointer.data(), &QObject::destroyed);
|
||||
QVERIFY(destroyedSpy.isValid());
|
||||
QSignalSpy regionChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::regionChanged);
|
||||
QVERIFY(regionChangedSpy.isValid());
|
||||
lockedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor));
|
||||
// it's double buffered
|
||||
QVERIFY(!regionChangedSpy.wait());
|
||||
surface->commit(Surface::CommitFlag::None);
|
||||
QVERIFY(regionChangedSpy.wait());
|
||||
QCOMPARE(serverLockedPointer->region(), QRegion(0, 5, 10, 20));
|
||||
|
||||
// let's lock the surface
|
||||
QSignalSpy lockedChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::lockedChanged);
|
||||
QVERIFY(lockedChangedSpy.isValid());
|
||||
m_seatInterface->setFocusedPointerSurface(serverSurface);
|
||||
QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion);
|
||||
QVERIFY(pointerMotionSpy.isValid());
|
||||
m_seatInterface->setPointerPos(QPoint(0, 1));
|
||||
QVERIFY(pointerMotionSpy.wait());
|
||||
|
||||
serverLockedPointer->setLocked(true);
|
||||
QCOMPARE(serverLockedPointer->isLocked(), true);
|
||||
m_seatInterface->setPointerPos(QPoint(1, 1));
|
||||
QCOMPARE(lockedChangedSpy.count(), 1);
|
||||
QCOMPARE(pointerMotionSpy.count(), 1);
|
||||
QVERIFY(lockedSpy.isEmpty());
|
||||
QVERIFY(lockedSpy.wait());
|
||||
QVERIFY(unlockedSpy.isEmpty());
|
||||
|
||||
// and unlock again
|
||||
serverLockedPointer->setLocked(false);
|
||||
QCOMPARE(serverLockedPointer->isLocked(), false);
|
||||
QCOMPARE(lockedChangedSpy.count(), 2);
|
||||
QTEST(!serverSurface->lockedPointer().isNull(), "hasConstraintAfterUnlock");
|
||||
QTEST(pointerConstraintsChangedSpy.count(), "pointerChangedCount");
|
||||
QVERIFY(unlockedSpy.wait());
|
||||
QCOMPARE(unlockedSpy.count(), 1);
|
||||
QCOMPARE(lockedSpy.count(), 1);
|
||||
|
||||
// now motion should work again
|
||||
m_seatInterface->setPointerPos(QPoint(0, 1));
|
||||
QVERIFY(pointerMotionSpy.wait());
|
||||
QCOMPARE(pointerMotionSpy.count(), 2);
|
||||
|
||||
lockedPointer.reset();
|
||||
QVERIFY(destroyedSpy.wait());
|
||||
QCOMPARE(pointerConstraintsChangedSpy.count(), 2);
|
||||
}
|
||||
|
||||
void TestPointerConstraints::testConfinePointer_data()
|
||||
{
|
||||
QTest::addColumn<PointerConstraints::LifeTime>("clientLifeTime");
|
||||
QTest::addColumn<ConfinedPointerInterface::LifeTime>("serverLifeTime");
|
||||
QTest::addColumn<bool>("hasConstraintAfterUnlock");
|
||||
QTest::addColumn<int>("pointerChangedCount");
|
||||
|
||||
QTest::newRow("persistent") << PointerConstraints::LifeTime::Persistent << ConfinedPointerInterface::LifeTime::Persistent << true << 1;
|
||||
QTest::newRow("oneshot") << PointerConstraints::LifeTime::OneShot << ConfinedPointerInterface::LifeTime::OneShot << false << 2;
|
||||
}
|
||||
|
||||
void TestPointerConstraints::testConfinePointer()
|
||||
{
|
||||
// this test verifies the basic interaction for confined pointer
|
||||
// first create a surface
|
||||
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
||||
QVERIFY(surfaceCreatedSpy.isValid());
|
||||
QScopedPointer<Surface> surface(m_compositor->createSurface());
|
||||
QVERIFY(surface->isValid());
|
||||
QVERIFY(surfaceCreatedSpy.wait());
|
||||
|
||||
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
|
||||
QVERIFY(serverSurface);
|
||||
QVERIFY(serverSurface->lockedPointer().isNull());
|
||||
QVERIFY(serverSurface->confinedPointer().isNull());
|
||||
|
||||
// now create the confined pointer
|
||||
QSignalSpy pointerConstraintsChangedSpy(serverSurface, &SurfaceInterface::pointerConstraintsChanged);
|
||||
QVERIFY(pointerConstraintsChangedSpy.isValid());
|
||||
QFETCH(PointerConstraints::LifeTime, clientLifeTime);
|
||||
QScopedPointer<ConfinedPointer> confinedPointer(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, clientLifeTime));
|
||||
QSignalSpy confinedSpy(confinedPointer.data(), &ConfinedPointer::confined);
|
||||
QVERIFY(confinedSpy.isValid());
|
||||
QSignalSpy unconfinedSpy(confinedPointer.data(), &ConfinedPointer::unconfined);
|
||||
QVERIFY(unconfinedSpy.isValid());
|
||||
QVERIFY(confinedPointer->isValid());
|
||||
QVERIFY(pointerConstraintsChangedSpy.wait());
|
||||
|
||||
auto serverConfinedPointer = serverSurface->confinedPointer();
|
||||
QVERIFY(serverConfinedPointer);
|
||||
QVERIFY(serverSurface->lockedPointer().isNull());
|
||||
|
||||
QCOMPARE(serverConfinedPointer->isConfined(), false);
|
||||
QCOMPARE(serverConfinedPointer->region(), QRegion());
|
||||
QFETCH(ConfinedPointerInterface::LifeTime, serverLifeTime);
|
||||
QCOMPARE(serverConfinedPointer->lifeTime(), serverLifeTime);
|
||||
// setting to unconfined now should not trigger an unconfined spy
|
||||
serverConfinedPointer->setConfined(false);
|
||||
QVERIFY(!unconfinedSpy.wait());
|
||||
|
||||
// try setting a region
|
||||
QSignalSpy destroyedSpy(serverConfinedPointer.data(), &QObject::destroyed);
|
||||
QVERIFY(destroyedSpy.isValid());
|
||||
QSignalSpy regionChangedSpy(serverConfinedPointer.data(), &ConfinedPointerInterface::regionChanged);
|
||||
QVERIFY(regionChangedSpy.isValid());
|
||||
confinedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor));
|
||||
// it's double buffered
|
||||
QVERIFY(!regionChangedSpy.wait());
|
||||
surface->commit(Surface::CommitFlag::None);
|
||||
QVERIFY(regionChangedSpy.wait());
|
||||
QCOMPARE(serverConfinedPointer->region(), QRegion(0, 5, 10, 20));
|
||||
|
||||
// let's confine the surface
|
||||
QSignalSpy confinedChangedSpy(serverConfinedPointer.data(), &ConfinedPointerInterface::confinedChanged);
|
||||
QVERIFY(confinedChangedSpy.isValid());
|
||||
m_seatInterface->setFocusedPointerSurface(serverSurface);
|
||||
serverConfinedPointer->setConfined(true);
|
||||
QCOMPARE(serverConfinedPointer->isConfined(), true);
|
||||
QCOMPARE(confinedChangedSpy.count(), 1);
|
||||
QVERIFY(confinedSpy.isEmpty());
|
||||
QVERIFY(confinedSpy.wait());
|
||||
QVERIFY(unconfinedSpy.isEmpty());
|
||||
|
||||
// and unconfine again
|
||||
serverConfinedPointer->setConfined(false);
|
||||
QCOMPARE(serverConfinedPointer->isConfined(), false);
|
||||
QCOMPARE(confinedChangedSpy.count(), 2);
|
||||
QTEST(!serverSurface->confinedPointer().isNull(), "hasConstraintAfterUnlock");
|
||||
QTEST(pointerConstraintsChangedSpy.count(), "pointerChangedCount");
|
||||
QVERIFY(unconfinedSpy.wait());
|
||||
QCOMPARE(unconfinedSpy.count(), 1);
|
||||
QCOMPARE(confinedSpy.count(), 1);
|
||||
|
||||
confinedPointer.reset();
|
||||
QVERIFY(destroyedSpy.wait());
|
||||
QCOMPARE(pointerConstraintsChangedSpy.count(), 2);
|
||||
}
|
||||
|
||||
enum class Constraint {
|
||||
Lock,
|
||||
Confine
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Constraint)
|
||||
|
||||
void TestPointerConstraints::testAlreadyConstrained_data()
|
||||
{
|
||||
QTest::addColumn<Constraint>("firstConstraint");
|
||||
QTest::addColumn<Constraint>("secondConstraint");
|
||||
|
||||
QTest::newRow("confine-confine") << Constraint::Confine << Constraint::Confine;
|
||||
QTest::newRow("lock-confine") << Constraint::Lock << Constraint::Confine;
|
||||
QTest::newRow("confine-lock") << Constraint::Confine << Constraint::Lock;
|
||||
QTest::newRow("lock-lock") << Constraint::Lock << Constraint::Lock;
|
||||
}
|
||||
|
||||
void TestPointerConstraints::testAlreadyConstrained()
|
||||
{
|
||||
// this test verifies that creating a pointer constraint for an already constrained surface triggers an error
|
||||
// first create a surface
|
||||
QScopedPointer<Surface> surface(m_compositor->createSurface());
|
||||
QVERIFY(surface->isValid());
|
||||
QFETCH(Constraint, firstConstraint);
|
||||
QScopedPointer<ConfinedPointer> confinedPointer;
|
||||
QScopedPointer<LockedPointer> lockedPointer;
|
||||
switch (firstConstraint) {
|
||||
case Constraint::Lock:
|
||||
lockedPointer.reset(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
|
||||
break;
|
||||
case Constraint::Confine:
|
||||
confinedPointer.reset(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
QVERIFY(confinedPointer || lockedPointer);
|
||||
|
||||
QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred);
|
||||
QVERIFY(errorSpy.isValid());
|
||||
QFETCH(Constraint, secondConstraint);
|
||||
QScopedPointer<ConfinedPointer> confinedPointer2;
|
||||
QScopedPointer<LockedPointer> lockedPointer2;
|
||||
switch (secondConstraint) {
|
||||
case Constraint::Lock:
|
||||
lockedPointer2.reset(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
|
||||
break;
|
||||
case Constraint::Confine:
|
||||
confinedPointer2.reset(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot));
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
QVERIFY(errorSpy.wait());
|
||||
QVERIFY(m_connection->hasError());
|
||||
if (confinedPointer2) {
|
||||
confinedPointer2->destroy();
|
||||
}
|
||||
if (lockedPointer2) {
|
||||
lockedPointer2->destroy();
|
||||
}
|
||||
if (confinedPointer) {
|
||||
confinedPointer->destroy();
|
||||
}
|
||||
if (lockedPointer) {
|
||||
lockedPointer->destroy();
|
||||
}
|
||||
surface->destroy();
|
||||
m_compositor->destroy();
|
||||
m_pointerConstraints->destroy();
|
||||
m_pointer->destroy();
|
||||
m_seat->destroy();
|
||||
m_queue->destroy();
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestPointerConstraints)
|
||||
#include "test_pointer_constraints.moc"
|
|
@ -26,6 +26,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "../../src/client/event_queue.h"
|
||||
#include "../../src/client/registry.h"
|
||||
#include "../../src/client/output.h"
|
||||
#include "../../src/client/pointerconstraints.h"
|
||||
#include "../../src/client/pointergestures.h"
|
||||
#include "../../src/client/seat.h"
|
||||
#include "../../src/client/relativepointer.h"
|
||||
|
@ -47,6 +48,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "../../src/server/subcompositor_interface.h"
|
||||
#include "../../src/server/outputmanagement_interface.h"
|
||||
#include "../../src/server/outputdevice_interface.h"
|
||||
#include "../../src/server/pointerconstraints_interface.h"
|
||||
#include "../../src/server/pointergestures_interface.h"
|
||||
#include "../../src/server/textinput_interface.h"
|
||||
#include "../../src/server/xdgshell_interface.h"
|
||||
|
@ -60,6 +62,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <wayland-xdg-shell-v5-client-protocol.h>
|
||||
#include <wayland-relativepointer-unstable-v1-client-protocol.h>
|
||||
#include <wayland-pointer-gestures-unstable-v1-client-protocol.h>
|
||||
#include <wayland-pointer-constraints-unstable-v1-client-protocol.h>
|
||||
|
||||
class TestWaylandRegistry : public QObject
|
||||
{
|
||||
|
@ -88,6 +91,7 @@ private Q_SLOTS:
|
|||
void testBindXdgShellUnstableV5();
|
||||
void testBindRelativePointerManagerUnstableV1();
|
||||
void testBindPointerGesturesUnstableV1();
|
||||
void testBindPointerConstraintsUnstableV1();
|
||||
void testGlobalSync();
|
||||
void testGlobalSyncThreaded();
|
||||
void testRemoval();
|
||||
|
@ -111,6 +115,7 @@ private:
|
|||
KWayland::Server::XdgShellInterface *m_xdgShellUnstableV5;
|
||||
KWayland::Server::RelativePointerManagerInterface *m_relativePointerV1;
|
||||
KWayland::Server::PointerGesturesInterface *m_pointerGesturesV1;
|
||||
KWayland::Server::PointerConstraintsInterface *m_pointerConstraintsV1;
|
||||
};
|
||||
|
||||
static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0");
|
||||
|
@ -132,6 +137,7 @@ TestWaylandRegistry::TestWaylandRegistry(QObject *parent)
|
|||
, m_xdgShellUnstableV5(nullptr)
|
||||
, m_relativePointerV1(nullptr)
|
||||
, m_pointerGesturesV1(nullptr)
|
||||
, m_pointerConstraintsV1(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -179,6 +185,9 @@ void TestWaylandRegistry::init()
|
|||
m_pointerGesturesV1 = m_display->createPointerGestures(KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1);
|
||||
m_pointerGesturesV1->create();
|
||||
QCOMPARE(m_pointerGesturesV1->interfaceVersion(), KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1);
|
||||
m_pointerConstraintsV1 = m_display->createPointerConstraints(KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1);
|
||||
m_pointerConstraintsV1->create();
|
||||
QCOMPARE(m_pointerConstraintsV1->interfaceVersion(), KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1);
|
||||
}
|
||||
|
||||
void TestWaylandRegistry::cleanup()
|
||||
|
@ -331,6 +340,11 @@ void TestWaylandRegistry::testBindPointerGesturesUnstableV1()
|
|||
TEST_BIND(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1, SIGNAL(pointerGesturesUnstableV1Announced(quint32,quint32)), bindPointerGesturesUnstableV1, zwp_pointer_gestures_v1_destroy)
|
||||
}
|
||||
|
||||
void TestWaylandRegistry::testBindPointerConstraintsUnstableV1()
|
||||
{
|
||||
TEST_BIND(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1, SIGNAL(pointerConstraintsUnstableV1Announced(quint32,quint32)), bindPointerConstraintsUnstableV1, zwp_pointer_constraints_v1_destroy)
|
||||
}
|
||||
|
||||
#undef TEST_BIND
|
||||
|
||||
void TestWaylandRegistry::testRemoval()
|
||||
|
|
|
@ -30,6 +30,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "output_interface.h"
|
||||
#include "plasmashell_interface.h"
|
||||
#include "plasmawindowmanagement_interface.h"
|
||||
#include "pointerconstraints_interface_p.h"
|
||||
#include "pointergestures_interface_p.h"
|
||||
#include "qtsurfaceextension_interface.h"
|
||||
#include "seat_interface.h"
|
||||
|
@ -395,6 +396,18 @@ PointerGesturesInterface *Display::createPointerGestures(const PointerGesturesIn
|
|||
return p;
|
||||
}
|
||||
|
||||
PointerConstraintsInterface *Display::createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent)
|
||||
{
|
||||
PointerConstraintsInterface *p = nullptr;
|
||||
switch (version) {
|
||||
case PointerConstraintsInterfaceVersion::UnstableV1:
|
||||
p = new PointerConstraintsUnstableV1Interface(this, parent);
|
||||
break;
|
||||
}
|
||||
connect(this, &Display::aboutToTerminate, p, [p] { delete p; });
|
||||
return p;
|
||||
}
|
||||
|
||||
void Display::createShm()
|
||||
{
|
||||
Q_ASSERT(d->display);
|
||||
|
|
|
@ -78,6 +78,8 @@ enum class RelativePointerInterfaceVersion;
|
|||
class RelativePointerManagerInterface;
|
||||
enum class PointerGesturesInterfaceVersion;
|
||||
class PointerGesturesInterface;
|
||||
enum class PointerConstraintsInterfaceVersion;
|
||||
class PointerConstraintsInterface;
|
||||
|
||||
/**
|
||||
* @brief Class holding the Wayland server display loop.
|
||||
|
@ -209,6 +211,14 @@ public:
|
|||
**/
|
||||
PointerGesturesInterface *createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Creates the PointerConstraintsInterface in interface @p version
|
||||
*
|
||||
* @returns The created manager object
|
||||
* @since 5.29
|
||||
**/
|
||||
PointerConstraintsInterface *createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Gets the ClientConnection for the given @p client.
|
||||
* If there is no ClientConnection yet for the given @p client, it will be created.
|
||||
|
|
|
@ -19,6 +19,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
*********************************************************************/
|
||||
#include "pointer_interface.h"
|
||||
#include "pointer_interface_p.h"
|
||||
#include "pointerconstraints_interface.h"
|
||||
#include "pointergestures_interface_p.h"
|
||||
#include "resource_p.h"
|
||||
#include "relativepointer_interface_p.h"
|
||||
|
@ -228,6 +229,9 @@ PointerInterface::PointerInterface(SeatInterface *parent, wl_resource *parentRes
|
|||
return;
|
||||
}
|
||||
if (d->focusedSurface && d->resource) {
|
||||
if (!d->focusedSurface->lockedPointer().isNull() && d->focusedSurface->lockedPointer()->isLocked()) {
|
||||
return;
|
||||
}
|
||||
const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos());
|
||||
auto targetSurface = d->focusedSurface->surfaceAt(pos);
|
||||
if (!targetSurface) {
|
||||
|
|
201
src/wayland/server/pointerconstraints_interface.cpp
Normal file
201
src/wayland/server/pointerconstraints_interface.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
/****************************************************************************
|
||||
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) version 3, or any
|
||||
later version accepted by the membership of KDE e.V. (or its
|
||||
successor approved by the membership of KDE e.V.), which shall
|
||||
act as a proxy defined in Section 6 of version 3 of the license.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
#include "pointerconstraints_interface_p.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Server
|
||||
{
|
||||
|
||||
PointerConstraintsInterface::Private::Private(PointerConstraintsInterfaceVersion interfaceVersion, PointerConstraintsInterface *q, Display *d, const wl_interface *interface, quint32 version)
|
||||
: Global::Private(d, interface, version)
|
||||
, interfaceVersion(interfaceVersion)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
PointerConstraintsInterface::PointerConstraintsInterface(Private *d, QObject *parent)
|
||||
: Global(d, parent)
|
||||
{
|
||||
}
|
||||
|
||||
PointerConstraintsInterface::~PointerConstraintsInterface() = default;
|
||||
|
||||
PointerConstraintsInterfaceVersion PointerConstraintsInterface::interfaceVersion() const
|
||||
{
|
||||
Q_D();
|
||||
return d->interfaceVersion;
|
||||
}
|
||||
|
||||
PointerConstraintsInterface::Private *PointerConstraintsInterface::d_func() const
|
||||
{
|
||||
return reinterpret_cast<Private*>(d.data());
|
||||
}
|
||||
|
||||
LockedPointerInterface::Private::Private(PointerConstraintsInterfaceVersion interfaceVersion, LockedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation)
|
||||
: Resource::Private(q, c, parentResource, interface, implementation)
|
||||
, interfaceVersion(interfaceVersion)
|
||||
{
|
||||
}
|
||||
|
||||
LockedPointerInterface::Private::~Private()
|
||||
{
|
||||
if (resource) {
|
||||
wl_resource_destroy(resource);
|
||||
resource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void LockedPointerInterface::Private::commit()
|
||||
{
|
||||
if (!regionIsSet) {
|
||||
return;
|
||||
}
|
||||
region = pendingRegion;
|
||||
pendingRegion = QRegion();
|
||||
regionIsSet = false;
|
||||
emit q_func()->regionChanged();
|
||||
}
|
||||
|
||||
LockedPointerInterface::LockedPointerInterface(Private *p, QObject *parent)
|
||||
: Resource(p, parent)
|
||||
{
|
||||
connect(this, &LockedPointerInterface::unbound, this, std::bind(&LockedPointerInterface::setLocked, this, false));
|
||||
}
|
||||
|
||||
LockedPointerInterface::~LockedPointerInterface() = default;
|
||||
|
||||
PointerConstraintsInterfaceVersion LockedPointerInterface::interfaceVersion() const
|
||||
{
|
||||
Q_D();
|
||||
return d->interfaceVersion;
|
||||
}
|
||||
|
||||
LockedPointerInterface::LifeTime LockedPointerInterface::lifeTime() const
|
||||
{
|
||||
Q_D();
|
||||
return d->lifeTime;
|
||||
}
|
||||
|
||||
QRegion LockedPointerInterface::region() const
|
||||
{
|
||||
Q_D();
|
||||
return d->region;
|
||||
}
|
||||
|
||||
bool LockedPointerInterface::isLocked() const
|
||||
{
|
||||
Q_D();
|
||||
return d->locked;
|
||||
}
|
||||
|
||||
void LockedPointerInterface::setLocked(bool locked)
|
||||
{
|
||||
Q_D();
|
||||
if (locked == d->locked) {
|
||||
return;
|
||||
}
|
||||
d->locked = locked;
|
||||
d->updateLocked();
|
||||
emit lockedChanged();
|
||||
}
|
||||
|
||||
LockedPointerInterface::Private *LockedPointerInterface::d_func() const
|
||||
{
|
||||
return reinterpret_cast<Private*>(d.data());
|
||||
}
|
||||
|
||||
ConfinedPointerInterface::Private::Private(PointerConstraintsInterfaceVersion interfaceVersion, ConfinedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation)
|
||||
: Resource::Private(q, c, parentResource, interface, implementation)
|
||||
, interfaceVersion(interfaceVersion)
|
||||
{
|
||||
}
|
||||
|
||||
ConfinedPointerInterface::Private::~Private()
|
||||
{
|
||||
if (resource) {
|
||||
wl_resource_destroy(resource);
|
||||
resource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfinedPointerInterface::Private::commit()
|
||||
{
|
||||
if (!regionIsSet) {
|
||||
return;
|
||||
}
|
||||
region = pendingRegion;
|
||||
pendingRegion = QRegion();
|
||||
regionIsSet = false;
|
||||
emit q_func()->regionChanged();
|
||||
}
|
||||
|
||||
ConfinedPointerInterface::ConfinedPointerInterface(Private *p, QObject *parent)
|
||||
: Resource(p, parent)
|
||||
{
|
||||
connect(this, &ConfinedPointerInterface::unbound, this, std::bind(&ConfinedPointerInterface::setConfined, this, false));
|
||||
}
|
||||
|
||||
ConfinedPointerInterface::~ConfinedPointerInterface() = default;
|
||||
|
||||
PointerConstraintsInterfaceVersion ConfinedPointerInterface::interfaceVersion() const
|
||||
{
|
||||
Q_D();
|
||||
return d->interfaceVersion;
|
||||
}
|
||||
|
||||
ConfinedPointerInterface::LifeTime ConfinedPointerInterface::lifeTime() const
|
||||
{
|
||||
Q_D();
|
||||
return d->lifeTime;
|
||||
}
|
||||
|
||||
QRegion ConfinedPointerInterface::region() const
|
||||
{
|
||||
Q_D();
|
||||
return d->region;
|
||||
}
|
||||
|
||||
bool ConfinedPointerInterface::isConfined() const
|
||||
{
|
||||
Q_D();
|
||||
return d->confined;
|
||||
}
|
||||
|
||||
void ConfinedPointerInterface::setConfined(bool confined)
|
||||
{
|
||||
Q_D();
|
||||
if (confined == d->confined) {
|
||||
return;
|
||||
}
|
||||
d->confined = confined;
|
||||
d->updateConfined();
|
||||
emit confinedChanged();
|
||||
}
|
||||
|
||||
ConfinedPointerInterface::Private *ConfinedPointerInterface::d_func() const
|
||||
{
|
||||
return reinterpret_cast<Private*>(d.data());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
268
src/wayland/server/pointerconstraints_interface.h
Normal file
268
src/wayland/server/pointerconstraints_interface.h
Normal file
|
@ -0,0 +1,268 @@
|
|||
/****************************************************************************
|
||||
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) version 3, or any
|
||||
later version accepted by the membership of KDE e.V. (or its
|
||||
successor approved by the membership of KDE e.V.), which shall
|
||||
act as a proxy defined in Section 6 of version 3 of the license.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
#ifndef KWAYLAND_SERVER_POINTERCONSTRAINTS_H
|
||||
#define KWAYLAND_SERVER_POINTERCONSTRAINTS_H
|
||||
|
||||
#include "global.h"
|
||||
#include "resource.h"
|
||||
|
||||
#include <KWayland/Server/kwaylandserver_export.h>
|
||||
|
||||
#include <QRegion>
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Server
|
||||
{
|
||||
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
|
||||
/**
|
||||
* Enum describing the interface versions the PointerConstraintsInterface can support.
|
||||
*
|
||||
* @since 5.29
|
||||
**/
|
||||
enum class PointerConstraintsInterfaceVersion {
|
||||
/**
|
||||
* zwp_pointer_constraints_v1
|
||||
**/
|
||||
UnstableV1
|
||||
};
|
||||
|
||||
/**
|
||||
* Manager object to create pointer constraints.
|
||||
*
|
||||
* To create this manager use @link{Display::createPointerConstraints}
|
||||
*
|
||||
* @see ConfinedPointerInterface
|
||||
* @see LockedPointerInterface
|
||||
* @see Display::createPointerConstraints
|
||||
* @since 5.29
|
||||
**/
|
||||
class KWAYLANDSERVER_EXPORT PointerConstraintsInterface : public Global
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~PointerConstraintsInterface();
|
||||
|
||||
/**
|
||||
* @returns The interface version used by this PointerConstraintsInterface
|
||||
**/
|
||||
PointerConstraintsInterfaceVersion interfaceVersion() const;
|
||||
|
||||
protected:
|
||||
class Private;
|
||||
explicit PointerConstraintsInterface(Private *d, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
Private *d_func() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The LockedPointerInterface lets the client request to disable movements of
|
||||
* the virtual pointer (i.e. the cursor), effectively locking the pointer
|
||||
* to a position.
|
||||
*
|
||||
* It is up to the compositor whether the lock gets activated.
|
||||
* To activate it needs to use @link{LockedPointerInterface::setLocked}.
|
||||
* The compositor needs to ensure that the SurfaceInterface has pointer focus
|
||||
* and that the pointer is inside the @link{LockedPointerInterface::region} when
|
||||
* it activates the lock.
|
||||
*
|
||||
* While the lock is active the PointerInterface does no longer emit pointer motion
|
||||
* events, but still emits relative pointer motion events.
|
||||
*
|
||||
* @since 5.29
|
||||
**/
|
||||
class KWAYLANDSERVER_EXPORT LockedPointerInterface : public Resource
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
virtual ~LockedPointerInterface();
|
||||
|
||||
/**
|
||||
* @returns The interface version used by this LockedPointerInterface
|
||||
**/
|
||||
PointerConstraintsInterfaceVersion interfaceVersion() const;
|
||||
|
||||
enum class LifeTime {
|
||||
OneShot,
|
||||
Persistent
|
||||
};
|
||||
|
||||
LifeTime lifeTime() const;
|
||||
|
||||
/**
|
||||
* The intersection of this region and the input region of the SurfaceInterface is used
|
||||
* to determine where the pointer must be in order for the lock to activate.
|
||||
* It is up to the compositor whether to warp the pointer or require some kind of
|
||||
* user interaction for the lock to activate.
|
||||
*
|
||||
* If the region is empty the SurfaceInterface input region is used.
|
||||
*
|
||||
* @see regionChanged
|
||||
* @see SurfaceInterface::input
|
||||
**/
|
||||
QRegion region() const;
|
||||
|
||||
/**
|
||||
* Whether the Compositor set this pointer lock to be active.
|
||||
* @see setLocked
|
||||
* @see lockedChanged
|
||||
**/
|
||||
bool isLocked() const;
|
||||
|
||||
/**
|
||||
* Activates or deactivates the lock.
|
||||
*
|
||||
* A pointer lock can only be activated if the SurfaceInterface
|
||||
* this LockedPointerInterface was created for has pointer focus
|
||||
* and the pointer is inside the @link{region}.
|
||||
*
|
||||
* @param locked Whether the lock should be active
|
||||
* @see isLocked
|
||||
* @see lockedChanged
|
||||
**/
|
||||
void setLocked(bool locked);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted whenever the region changes.
|
||||
* This happens when the parent SurfaceInterface gets committed
|
||||
* @see region
|
||||
**/
|
||||
void regionChanged();
|
||||
|
||||
/**
|
||||
* Emitted whenever the @link{isLocked} state changes.
|
||||
* @see isLocked
|
||||
* @see setLocked
|
||||
**/
|
||||
void lockedChanged();
|
||||
|
||||
protected:
|
||||
class Private;
|
||||
explicit LockedPointerInterface(Private *p, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
Private *d_func() const;
|
||||
friend class SurfaceInterface;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* The ConfinedPointerInterface gets installed on a SurfaceInterface.
|
||||
* The confinement indicates that the SurfaceInterface wants to confine the
|
||||
* pointer to a region of the SurfaceInterface.
|
||||
*
|
||||
* It is up to the compositor whether the confinement gets activated.
|
||||
* To activate it needs to use @link{ConfinedPointerInterface::setConfined}.
|
||||
* The compositor needs to ensure that the SurfaceInterface has pointer focus
|
||||
* and that the pointer is inside the @link{ConfinedPointerInterface::region} when
|
||||
* it activates the confinement.
|
||||
*
|
||||
* From client side the confinement gets deactivated by destroying the ConfinedPointerInterface.
|
||||
* From compositor side the confinement can be deactivated by setting
|
||||
* @link{ConfinedPointerInterface::setConfined} to @c false.
|
||||
*
|
||||
* @since 5.29
|
||||
**/
|
||||
class KWAYLANDSERVER_EXPORT ConfinedPointerInterface : public Resource
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
virtual ~ConfinedPointerInterface();
|
||||
|
||||
/**
|
||||
* @returns The interface version used by this ConfinedPointerInterface
|
||||
**/
|
||||
PointerConstraintsInterfaceVersion interfaceVersion() const;
|
||||
|
||||
enum class LifeTime {
|
||||
OneShot,
|
||||
Persistent
|
||||
};
|
||||
|
||||
LifeTime lifeTime() const;
|
||||
|
||||
/**
|
||||
* The intersection of this region and the input region of the SurfaceInterface is used
|
||||
* to determine where the pointer must be in order for the confinement to activate.
|
||||
* It is up to the compositor whether to warp the pointer or require some kind of
|
||||
* user interaction for the confinement to activate.
|
||||
*
|
||||
* If the region is empty the SurfaceInterface input region is used.
|
||||
*
|
||||
* @see regionChanged
|
||||
* @see SurfaceInterface::input
|
||||
**/
|
||||
QRegion region() const;
|
||||
|
||||
/**
|
||||
* Whether the Compositor set this pointer confinement to be active.
|
||||
* @see setConfined
|
||||
* @see confinedChanged
|
||||
**/
|
||||
bool isConfined() const;
|
||||
|
||||
/**
|
||||
* Activates or deactivates the confinement.
|
||||
*
|
||||
* A pointer confinement can only be activated if the SurfaceInterface
|
||||
* this ConfinedPointerInterface was created for has pointer focus
|
||||
* and the pointer is inside the @link{region}.
|
||||
*
|
||||
* @param confined Whether the confinement should be active
|
||||
* @see isConfined
|
||||
* @see confinedChanged
|
||||
**/
|
||||
void setConfined(bool confined);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted whenever the region changes.
|
||||
* This happens when the parent SurfaceInterface gets committed
|
||||
* @see region
|
||||
**/
|
||||
void regionChanged();
|
||||
|
||||
/**
|
||||
* Emitted whenever the @link{isConfined} state changes.
|
||||
* @see isConfined
|
||||
* @see setConfined
|
||||
**/
|
||||
void confinedChanged();
|
||||
|
||||
protected:
|
||||
class Private;
|
||||
explicit ConfinedPointerInterface(Private *p, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
Private *d_func() const;
|
||||
friend class SurfaceInterface;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
136
src/wayland/server/pointerconstraints_interface_p.h
Normal file
136
src/wayland/server/pointerconstraints_interface_p.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
/****************************************************************************
|
||||
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) version 3, or any
|
||||
later version accepted by the membership of KDE e.V. (or its
|
||||
successor approved by the membership of KDE e.V.), which shall
|
||||
act as a proxy defined in Section 6 of version 3 of the license.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
#ifndef KWAYLAND_SERVER_POINTERCONSTRAINTS_P_H
|
||||
#define KWAYLAND_SERVER_POINTERCONSTRAINTS_P_H
|
||||
#include "pointerconstraints_interface.h"
|
||||
#include "global_p.h"
|
||||
#include "resource_p.h"
|
||||
|
||||
#include <QRegion>
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Server
|
||||
{
|
||||
|
||||
class PointerConstraintsInterface::Private : public Global::Private
|
||||
{
|
||||
public:
|
||||
PointerConstraintsInterfaceVersion interfaceVersion;
|
||||
|
||||
protected:
|
||||
Private(PointerConstraintsInterfaceVersion interfaceVersion, PointerConstraintsInterface *q, Display *d, const wl_interface *interface, quint32 version);
|
||||
PointerConstraintsInterface *q;
|
||||
};
|
||||
|
||||
class PointerConstraintsUnstableV1Interface : public PointerConstraintsInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PointerConstraintsUnstableV1Interface(Display *display, QObject *parent = nullptr);
|
||||
virtual ~PointerConstraintsUnstableV1Interface();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
};
|
||||
|
||||
class LockedPointerInterface::Private : public Resource::Private
|
||||
{
|
||||
public:
|
||||
~Private();
|
||||
|
||||
virtual void updateLocked() = 0;
|
||||
void commit();
|
||||
|
||||
PointerConstraintsInterfaceVersion interfaceVersion;
|
||||
|
||||
LifeTime lifeTime;
|
||||
QRegion region;
|
||||
bool locked = false;
|
||||
|
||||
protected:
|
||||
Private(PointerConstraintsInterfaceVersion interfaceVersion, LockedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation);
|
||||
|
||||
QRegion pendingRegion;
|
||||
bool regionIsSet = false;
|
||||
|
||||
private:
|
||||
LockedPointerInterface *q_func() {
|
||||
return reinterpret_cast<LockedPointerInterface *>(q);
|
||||
}
|
||||
};
|
||||
|
||||
class LockedPointerUnstableV1Interface : public LockedPointerInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LockedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource);
|
||||
virtual ~LockedPointerUnstableV1Interface();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d_func() const;
|
||||
friend class PointerConstraintsUnstableV1Interface;
|
||||
};
|
||||
|
||||
class ConfinedPointerInterface::Private : public Resource::Private
|
||||
{
|
||||
public:
|
||||
~Private();
|
||||
|
||||
virtual void updateConfined() = 0;
|
||||
void commit();
|
||||
|
||||
PointerConstraintsInterfaceVersion interfaceVersion;
|
||||
|
||||
LifeTime lifeTime;
|
||||
QRegion region;
|
||||
|
||||
bool confined = false;
|
||||
|
||||
protected:
|
||||
Private(PointerConstraintsInterfaceVersion interfaceVersion, ConfinedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation);
|
||||
|
||||
QRegion pendingRegion;
|
||||
bool regionIsSet = false;
|
||||
|
||||
private:
|
||||
ConfinedPointerInterface *q_func() {
|
||||
return reinterpret_cast<ConfinedPointerInterface *>(q);
|
||||
}
|
||||
};
|
||||
|
||||
class ConfinedPointerUnstableV1Interface : public ConfinedPointerInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ConfinedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource);
|
||||
virtual ~ConfinedPointerUnstableV1Interface();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d_func() const;
|
||||
friend class PointerConstraintsUnstableV1Interface;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
287
src/wayland/server/pointerconstraints_interface_v1.cpp
Normal file
287
src/wayland/server/pointerconstraints_interface_v1.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
/****************************************************************************
|
||||
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) version 3, or any
|
||||
later version accepted by the membership of KDE e.V. (or its
|
||||
successor approved by the membership of KDE e.V.), which shall
|
||||
act as a proxy defined in Section 6 of version 3 of the license.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
#include "pointerconstraints_interface_p.h"
|
||||
#include "display.h"
|
||||
#include "pointer_interface.h"
|
||||
#include "region_interface.h"
|
||||
#include "surface_interface_p.h"
|
||||
|
||||
#include <wayland-pointer-constraints-unstable-v1-server-protocol.h>
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Server
|
||||
{
|
||||
|
||||
class PointerConstraintsUnstableV1Interface::Private : public PointerConstraintsInterface::Private
|
||||
{
|
||||
public:
|
||||
Private(PointerConstraintsUnstableV1Interface *q, Display *d);
|
||||
|
||||
private:
|
||||
void bind(wl_client *client, uint32_t version, uint32_t id) override;
|
||||
|
||||
template <class T>
|
||||
void createConstraint(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime);
|
||||
|
||||
static void unbind(wl_resource *resource);
|
||||
static Private *cast(wl_resource *r) {
|
||||
return reinterpret_cast<Private*>(wl_resource_get_user_data(r));
|
||||
}
|
||||
|
||||
static void destroyCallback(wl_client *client, wl_resource *resource);
|
||||
static void lockPointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * pointer, wl_resource * region, uint32_t lifetime);
|
||||
static void confinePointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * pointer, wl_resource * region, uint32_t lifetime);
|
||||
|
||||
PointerConstraintsUnstableV1Interface *q;
|
||||
static const struct zwp_pointer_constraints_v1_interface s_interface;
|
||||
static const quint32 s_version;
|
||||
};
|
||||
|
||||
class LockedPointerUnstableV1Interface::Private : public LockedPointerInterface::Private
|
||||
{
|
||||
public:
|
||||
Private(LockedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource);
|
||||
~Private();
|
||||
|
||||
void updateLocked() override;
|
||||
|
||||
private:
|
||||
static void setCursorPositionHintCallback(wl_client *client, wl_resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y);
|
||||
static void setRegionCallback(wl_client *client, wl_resource *resource, wl_resource * region);
|
||||
|
||||
LockedPointerUnstableV1Interface *q_func() {
|
||||
return reinterpret_cast<LockedPointerUnstableV1Interface *>(q);
|
||||
}
|
||||
|
||||
static const struct zwp_locked_pointer_v1_interface s_interface;
|
||||
};
|
||||
|
||||
const quint32 PointerConstraintsUnstableV1Interface::Private::s_version = 1;
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
const struct zwp_pointer_constraints_v1_interface PointerConstraintsUnstableV1Interface::Private::s_interface = {
|
||||
destroyCallback,
|
||||
lockPointerCallback,
|
||||
confinePointerCallback
|
||||
};
|
||||
#endif
|
||||
|
||||
void PointerConstraintsUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource)
|
||||
{
|
||||
Q_UNUSED(client)
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void PointerConstraintsUnstableV1Interface::Private::createConstraint(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime)
|
||||
{
|
||||
auto s = SurfaceInterface::get(surface);
|
||||
auto p = PointerInterface::get(pointer);
|
||||
if (!s || !p) {
|
||||
// send error?
|
||||
return;
|
||||
}
|
||||
if (!s->lockedPointer().isNull() || !s->confinedPointer().isNull()) {
|
||||
wl_resource_post_error(s->resource(), ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED, "Surface already constrained");
|
||||
return;
|
||||
}
|
||||
auto constraint = new T(q, resource);
|
||||
switch (lifetime) {
|
||||
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT:
|
||||
constraint->d_func()->lifeTime = T::LifeTime::Persistent;
|
||||
break;
|
||||
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT: // fall through
|
||||
default:
|
||||
constraint->d_func()->lifeTime = T::LifeTime::OneShot;
|
||||
break;
|
||||
}
|
||||
auto r = RegionInterface::get(region);
|
||||
constraint->d_func()->region = r ? r->region() : QRegion();
|
||||
constraint->d_func()->create(display->getConnection(client), version, id);
|
||||
s->d_func()->installPointerConstraint(constraint);
|
||||
}
|
||||
|
||||
void PointerConstraintsUnstableV1Interface::Private::lockPointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime)
|
||||
{
|
||||
cast(resource)->createConstraint<LockedPointerUnstableV1Interface>(client, resource, id, surface, pointer, region, lifetime);
|
||||
}
|
||||
|
||||
void PointerConstraintsUnstableV1Interface::Private::confinePointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime)
|
||||
{
|
||||
cast(resource)->createConstraint<ConfinedPointerUnstableV1Interface>(client, resource, id, surface, pointer, region, lifetime);
|
||||
}
|
||||
|
||||
PointerConstraintsUnstableV1Interface::Private::Private(PointerConstraintsUnstableV1Interface *q, Display *d)
|
||||
: PointerConstraintsInterface::Private(PointerConstraintsInterfaceVersion::UnstableV1, q, d, &zwp_pointer_constraints_v1_interface, s_version)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
void PointerConstraintsUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
|
||||
{
|
||||
auto c = display->getConnection(client);
|
||||
wl_resource *resource = c->createResource(&zwp_pointer_constraints_v1_interface, qMin(version, s_version), id);
|
||||
if (!resource) {
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
wl_resource_set_implementation(resource, &s_interface, this, unbind);
|
||||
// TODO: should we track?
|
||||
}
|
||||
|
||||
void PointerConstraintsUnstableV1Interface::Private::unbind(wl_resource *resource)
|
||||
{
|
||||
Q_UNUSED(resource)
|
||||
// TODO: implement?
|
||||
}
|
||||
|
||||
PointerConstraintsUnstableV1Interface::PointerConstraintsUnstableV1Interface(Display *display, QObject *parent)
|
||||
: PointerConstraintsInterface(new Private(this, display), parent)
|
||||
{
|
||||
}
|
||||
|
||||
PointerConstraintsUnstableV1Interface::~PointerConstraintsUnstableV1Interface() = default;
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
const struct zwp_locked_pointer_v1_interface LockedPointerUnstableV1Interface::Private::s_interface = {
|
||||
resourceDestroyedCallback,
|
||||
setCursorPositionHintCallback,
|
||||
setRegionCallback
|
||||
};
|
||||
#endif
|
||||
|
||||
void LockedPointerUnstableV1Interface::Private::setCursorPositionHintCallback(wl_client *client, wl_resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||
{
|
||||
Q_UNUSED(client)
|
||||
Q_UNUSED(resource)
|
||||
Q_UNUSED(surface_x)
|
||||
Q_UNUSED(surface_y)
|
||||
// double buffered
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void LockedPointerUnstableV1Interface::Private::setRegionCallback(wl_client *client, wl_resource *resource, wl_resource * region)
|
||||
{
|
||||
Q_UNUSED(client)
|
||||
auto p = cast<Private>(resource);
|
||||
auto r = RegionInterface::get(region);
|
||||
p->pendingRegion = r ? r->region() : QRegion();
|
||||
p->regionIsSet = true;
|
||||
}
|
||||
|
||||
void LockedPointerUnstableV1Interface::Private::updateLocked()
|
||||
{
|
||||
if (!resource) {
|
||||
return;
|
||||
}
|
||||
if (locked) {
|
||||
zwp_locked_pointer_v1_send_locked(resource);
|
||||
} else {
|
||||
zwp_locked_pointer_v1_send_unlocked(resource);
|
||||
}
|
||||
}
|
||||
|
||||
LockedPointerUnstableV1Interface::Private::Private(LockedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource)
|
||||
: LockedPointerInterface::Private(PointerConstraintsInterfaceVersion::UnstableV1, q, c, parentResource, &zwp_locked_pointer_v1_interface, &s_interface)
|
||||
{
|
||||
}
|
||||
|
||||
LockedPointerUnstableV1Interface::LockedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource)
|
||||
: LockedPointerInterface(new Private(this, parent, parentResource))
|
||||
{
|
||||
}
|
||||
|
||||
LockedPointerUnstableV1Interface::Private::~Private() = default;
|
||||
|
||||
LockedPointerUnstableV1Interface::~LockedPointerUnstableV1Interface() = default;
|
||||
|
||||
LockedPointerUnstableV1Interface::Private *LockedPointerUnstableV1Interface::d_func() const
|
||||
{
|
||||
return reinterpret_cast<Private*>(d.data());
|
||||
}
|
||||
|
||||
class ConfinedPointerUnstableV1Interface::Private : public ConfinedPointerInterface::Private
|
||||
{
|
||||
public:
|
||||
Private(ConfinedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource);
|
||||
~Private();
|
||||
|
||||
void updateConfined() override;
|
||||
|
||||
private:
|
||||
static void setRegionCallback(wl_client *client, wl_resource *resource, wl_resource * region);
|
||||
|
||||
ConfinedPointerUnstableV1Interface *q_func() {
|
||||
return reinterpret_cast<ConfinedPointerUnstableV1Interface *>(q);
|
||||
}
|
||||
|
||||
static const struct zwp_confined_pointer_v1_interface s_interface;
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
const struct zwp_confined_pointer_v1_interface ConfinedPointerUnstableV1Interface::Private::s_interface = {
|
||||
resourceDestroyedCallback,
|
||||
setRegionCallback
|
||||
};
|
||||
#endif
|
||||
|
||||
void ConfinedPointerUnstableV1Interface::Private::setRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region)
|
||||
{
|
||||
Q_UNUSED(client)
|
||||
auto p = cast<Private>(resource);
|
||||
auto r = RegionInterface::get(region);
|
||||
p->pendingRegion = r ? r->region() : QRegion();
|
||||
p->regionIsSet = true;
|
||||
}
|
||||
|
||||
ConfinedPointerUnstableV1Interface::Private::Private(ConfinedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource)
|
||||
: ConfinedPointerInterface::Private(PointerConstraintsInterfaceVersion::UnstableV1, q, c, parentResource, &zwp_confined_pointer_v1_interface, &s_interface)
|
||||
{
|
||||
}
|
||||
|
||||
ConfinedPointerUnstableV1Interface::ConfinedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource)
|
||||
: ConfinedPointerInterface(new Private(this, parent, parentResource))
|
||||
{
|
||||
}
|
||||
|
||||
ConfinedPointerUnstableV1Interface::Private::~Private() = default;
|
||||
|
||||
ConfinedPointerUnstableV1Interface::~ConfinedPointerUnstableV1Interface() = default;
|
||||
|
||||
void ConfinedPointerUnstableV1Interface::Private::updateConfined()
|
||||
{
|
||||
if (!resource) {
|
||||
return;
|
||||
}
|
||||
if (confined) {
|
||||
zwp_confined_pointer_v1_send_confined(resource);
|
||||
} else {
|
||||
zwp_confined_pointer_v1_send_unconfined(resource);
|
||||
}
|
||||
}
|
||||
|
||||
ConfinedPointerUnstableV1Interface::Private *ConfinedPointerUnstableV1Interface::d_func() const
|
||||
{
|
||||
return reinterpret_cast<Private*>(d.data());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "buffer_interface.h"
|
||||
#include "clientconnection.h"
|
||||
#include "compositor_interface.h"
|
||||
#include "pointerconstraints_interface_p.h"
|
||||
#include "region_interface.h"
|
||||
#include "subcompositor_interface.h"
|
||||
#include "subsurface_interface_p.h"
|
||||
|
@ -174,6 +175,82 @@ void SurfaceInterface::Private::setContrast(const QPointer<ContrastInterface> &c
|
|||
pending.contrastIsSet = true;
|
||||
}
|
||||
|
||||
void SurfaceInterface::Private::installPointerConstraint(LockedPointerInterface *lock)
|
||||
{
|
||||
Q_ASSERT(lockedPointer.isNull());
|
||||
Q_ASSERT(confinedPointer.isNull());
|
||||
lockedPointer = QPointer<LockedPointerInterface>(lock);
|
||||
if (lock->lifeTime() == LockedPointerInterface::LifeTime::OneShot) {
|
||||
constrainsOneShotConnection = QObject::connect(lock, &LockedPointerInterface::lockedChanged, q_func(),
|
||||
[this] {
|
||||
if (lockedPointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!lockedPointer->isLocked()) {
|
||||
lockedPointer.clear();
|
||||
disconnect(constrainsOneShotConnection);
|
||||
constrainsOneShotConnection = QMetaObject::Connection();
|
||||
disconnect(constrainsUnboundConnection);
|
||||
constrainsUnboundConnection = QMetaObject::Connection();
|
||||
emit q_func()->pointerConstraintsChanged();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
constrainsUnboundConnection = QObject::connect(lock, &LockedPointerInterface::unbound, q_func(),
|
||||
[this] {
|
||||
if (lockedPointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
lockedPointer.clear();
|
||||
disconnect(constrainsOneShotConnection);
|
||||
constrainsOneShotConnection = QMetaObject::Connection();
|
||||
disconnect(constrainsUnboundConnection);
|
||||
constrainsUnboundConnection = QMetaObject::Connection();
|
||||
emit q_func()->pointerConstraintsChanged();
|
||||
}
|
||||
);
|
||||
emit q_func()->pointerConstraintsChanged();
|
||||
}
|
||||
|
||||
void SurfaceInterface::Private::installPointerConstraint(ConfinedPointerInterface *confinement)
|
||||
{
|
||||
Q_ASSERT(lockedPointer.isNull());
|
||||
Q_ASSERT(confinedPointer.isNull());
|
||||
confinedPointer = QPointer<ConfinedPointerInterface>(confinement);
|
||||
if (confinement->lifeTime() == ConfinedPointerInterface::LifeTime::OneShot) {
|
||||
constrainsOneShotConnection = QObject::connect(confinement, &ConfinedPointerInterface::confinedChanged, q_func(),
|
||||
[this] {
|
||||
if (confinedPointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!confinedPointer->isConfined()) {
|
||||
confinedPointer.clear();
|
||||
disconnect(constrainsOneShotConnection);
|
||||
constrainsOneShotConnection = QMetaObject::Connection();
|
||||
disconnect(constrainsUnboundConnection);
|
||||
constrainsUnboundConnection = QMetaObject::Connection();
|
||||
emit q_func()->pointerConstraintsChanged();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
constrainsUnboundConnection = QObject::connect(confinement, &ConfinedPointerInterface::unbound, q_func(),
|
||||
[this] {
|
||||
if (confinedPointer.isNull()) {
|
||||
return;
|
||||
}
|
||||
confinedPointer.clear();
|
||||
disconnect(constrainsOneShotConnection);
|
||||
constrainsOneShotConnection = QMetaObject::Connection();
|
||||
disconnect(constrainsUnboundConnection);
|
||||
constrainsUnboundConnection = QMetaObject::Connection();
|
||||
emit q_func()->pointerConstraintsChanged();
|
||||
}
|
||||
);
|
||||
emit q_func()->pointerConstraintsChanged();
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
const struct wl_surface_interface SurfaceInterface::Private::s_interface = {
|
||||
resourceDestroyedCallback,
|
||||
|
@ -324,6 +401,12 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em
|
|||
target->transform = source->transform;
|
||||
target->transformIsSet = true;
|
||||
}
|
||||
if (!lockedPointer.isNull()) {
|
||||
lockedPointer->d_func()->commit();
|
||||
}
|
||||
if (!confinedPointer.isNull()) {
|
||||
confinedPointer->d_func()->commit();
|
||||
}
|
||||
|
||||
*source = State{};
|
||||
source->children = target->children;
|
||||
|
@ -755,6 +838,18 @@ SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
QPointer<LockedPointerInterface> SurfaceInterface::lockedPointer() const
|
||||
{
|
||||
Q_D();
|
||||
return d->lockedPointer;
|
||||
}
|
||||
|
||||
QPointer<ConfinedPointerInterface> SurfaceInterface::confinedPointer() const
|
||||
{
|
||||
Q_D();
|
||||
return d->confinedPointer;
|
||||
}
|
||||
|
||||
SurfaceInterface::Private *SurfaceInterface::d_func() const
|
||||
{
|
||||
return reinterpret_cast<Private*>(d.data());
|
||||
|
|
|
@ -36,9 +36,12 @@ namespace Server
|
|||
class BlurManagerInterface;
|
||||
class BlurInterface;
|
||||
class BufferInterface;
|
||||
class ConfinedPointerInterface;
|
||||
class ContrastInterface;
|
||||
class ContrastManagerInterface;
|
||||
class CompositorInterface;
|
||||
class LockedPointerInterface;
|
||||
class PointerConstraintsUnstableV1Interface;
|
||||
class ShadowManagerInterface;
|
||||
class ShadowInterface;
|
||||
class SlideInterface;
|
||||
|
@ -225,6 +228,20 @@ public:
|
|||
**/
|
||||
QVector<OutputInterface *> outputs() const;
|
||||
|
||||
/**
|
||||
* Pointer confinement installed on this SurfaceInterface.
|
||||
* @see pointerConstraintsChanged
|
||||
* @since 5.29
|
||||
**/
|
||||
QPointer<ConfinedPointerInterface> confinedPointer() const;
|
||||
|
||||
/**
|
||||
* Pointer lock installed on this SurfaceInterface.
|
||||
* @see pointerConstraintsChanged
|
||||
* @since 5.29
|
||||
**/
|
||||
QPointer<LockedPointerInterface> lockedPointer() const;
|
||||
|
||||
/**
|
||||
* @returns The SurfaceInterface for the @p native resource.
|
||||
**/
|
||||
|
@ -279,6 +296,18 @@ Q_SIGNALS:
|
|||
**/
|
||||
void subSurfaceTreeChanged();
|
||||
|
||||
/**
|
||||
* Emitted whenever a pointer constraint get (un)installed on this SurfaceInterface.
|
||||
*
|
||||
* The pointer constraint does not get activated, the compositor needs to activate
|
||||
* the lock/confinement.
|
||||
*
|
||||
* @see confinedPointer
|
||||
* @see lockedPointer
|
||||
* @since 5.29
|
||||
**/
|
||||
void pointerConstraintsChanged();
|
||||
|
||||
private:
|
||||
friend class CompositorInterface;
|
||||
friend class SubSurfaceInterface;
|
||||
|
@ -286,6 +315,7 @@ private:
|
|||
friend class BlurManagerInterface;
|
||||
friend class SlideManagerInterface;
|
||||
friend class ContrastManagerInterface;
|
||||
friend class PointerConstraintsUnstableV1Interface;
|
||||
explicit SurfaceInterface(CompositorInterface *parent, wl_resource *parentResource);
|
||||
|
||||
class Private;
|
||||
|
|
|
@ -75,6 +75,8 @@ public:
|
|||
void setBlur(const QPointer<BlurInterface> &blur);
|
||||
void setContrast(const QPointer<ContrastInterface> &contrast);
|
||||
void setSlide(const QPointer<SlideInterface> &slide);
|
||||
void installPointerConstraint(LockedPointerInterface *lock);
|
||||
void installPointerConstraint(ConfinedPointerInterface *confinement);
|
||||
|
||||
void commitSubSurface();
|
||||
void commit();
|
||||
|
@ -93,7 +95,13 @@ public:
|
|||
|
||||
QVector<OutputInterface *> outputs;
|
||||
|
||||
QPointer<LockedPointerInterface> lockedPointer;
|
||||
QPointer<ConfinedPointerInterface> confinedPointer;
|
||||
|
||||
private:
|
||||
QMetaObject::Connection constrainsOneShotConnection;
|
||||
QMetaObject::Connection constrainsUnboundConnection;
|
||||
|
||||
SurfaceInterface *q_func() {
|
||||
return reinterpret_cast<SurfaceInterface *>(q);
|
||||
}
|
||||
|
|
|
@ -52,3 +52,6 @@ zwp_relative_pointer_v1;RelativePointerUnstableV1
|
|||
zwp_pointer_gestures_v1;PointerGesturesUnstableV1
|
||||
zwp_pointer_gesture_swipe_v1;PointerSwipeGestureUnstableV1
|
||||
zwp_pointer_gesture_pinch_v1;PointerPinchGestureUnstableV1
|
||||
zwp_pointer_constraints_v1;PointerConstraints
|
||||
zwp_locked_pointer_v1;LockedPointer
|
||||
zwp_confined_pointer_v1;ConfinedPointer
|
||||
|
|
Loading…
Reference in a new issue