Add support for Surface enter/leave events
Summary: This change implements support for the wl_surface events enter and leave. Those events are emitted whenever a surface becomes visible on an output by e.g. mapping the surface, moving or resizing it. Similar the leave is sent whenever the surface is no longer on an output. The server side is not yet fully complete yet. It also needs to emit when the client binds the output another time and needs to send a leave before destroying the output. Reviewers: #plasma_on_wayland Subscribers: plasma-devel Tags: #plasma_on_wayland Differential Revision: https://phabricator.kde.org/D2528
This commit is contained in:
parent
2cf09cb50d
commit
9f92a05f52
6 changed files with 152 additions and 0 deletions
|
@ -25,6 +25,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "../../src/client/compositor.h"
|
||||
#include "../../src/client/connection_thread.h"
|
||||
#include "../../src/client/event_queue.h"
|
||||
#include "../../src/client/output.h"
|
||||
#include "../../src/client/surface.h"
|
||||
#include "../../src/client/region.h"
|
||||
#include "../../src/client/registry.h"
|
||||
|
@ -59,6 +60,7 @@ private Q_SLOTS:
|
|||
void testSurfaceAt();
|
||||
void testDestroyAttachedBuffer();
|
||||
void testDestroyWithPendingCallback();
|
||||
void testOutput();
|
||||
void testDisconnect();
|
||||
|
||||
private:
|
||||
|
@ -956,5 +958,78 @@ void TestWaylandSurface::testDisconnect()
|
|||
m_queue->destroy();
|
||||
}
|
||||
|
||||
void TestWaylandSurface::testOutput()
|
||||
{
|
||||
// This test verifies that the enter/leave are sent correctly to the Client
|
||||
using namespace KWayland::Client;
|
||||
using namespace KWayland::Server;
|
||||
qRegisterMetaType<KWayland::Client::Output*>();
|
||||
QScopedPointer<Surface> s(m_compositor->createSurface());
|
||||
QVERIFY(!s.isNull());
|
||||
QVERIFY(s->isValid());
|
||||
QVERIFY(s->outputs().isEmpty());
|
||||
QSignalSpy enteredSpy(s.data(), &Surface::outputEntered);
|
||||
QVERIFY(enteredSpy.isValid());
|
||||
QSignalSpy leftSpy(s.data(), &Surface::outputLeft);
|
||||
QVERIFY(leftSpy.isValid());
|
||||
// wait for the surface on the Server side
|
||||
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
||||
QVERIFY(surfaceCreatedSpy.isValid());
|
||||
QVERIFY(surfaceCreatedSpy.wait());
|
||||
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
|
||||
QVERIFY(serverSurface);
|
||||
QCOMPARE(serverSurface->outputs(), QVector<OutputInterface*>());
|
||||
|
||||
// create another registry to get notified about added outputs
|
||||
Registry registry;
|
||||
registry.setEventQueue(m_queue);
|
||||
QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced);
|
||||
QVERIFY(allAnnounced.isValid());
|
||||
registry.create(m_connection);
|
||||
QVERIFY(registry.isValid());
|
||||
registry.setup();
|
||||
QVERIFY(allAnnounced.wait());
|
||||
QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced);
|
||||
QVERIFY(outputAnnouncedSpy.isValid());
|
||||
|
||||
auto serverOutput = m_display->createOutput(m_display);
|
||||
serverOutput->create();
|
||||
QVERIFY(outputAnnouncedSpy.wait());
|
||||
QScopedPointer<Output> clientOutput(registry.createOutput(outputAnnouncedSpy.first().first().value<quint32>(), outputAnnouncedSpy.first().last().value<quint32>()));
|
||||
QVERIFY(clientOutput->isValid());
|
||||
m_connection->flush();
|
||||
m_display->dispatchEvents();
|
||||
|
||||
// now enter it
|
||||
serverSurface->setOutputs(QVector<OutputInterface*>{serverOutput});
|
||||
QCOMPARE(serverSurface->outputs(), QVector<OutputInterface*>{serverOutput});
|
||||
QVERIFY(enteredSpy.wait());
|
||||
QCOMPARE(enteredSpy.count(), 1);
|
||||
QCOMPARE(enteredSpy.first().first().value<Output*>(), clientOutput.data());
|
||||
QCOMPARE(s->outputs(), QVector<Output*>{clientOutput.data()});
|
||||
|
||||
// adding to same should not trigger
|
||||
serverSurface->setOutputs(QVector<OutputInterface*>{serverOutput});
|
||||
|
||||
// leave again
|
||||
serverSurface->setOutputs(QVector<OutputInterface*>());
|
||||
QCOMPARE(serverSurface->outputs(), QVector<OutputInterface*>());
|
||||
QVERIFY(leftSpy.wait());
|
||||
QCOMPARE(enteredSpy.count(), 1);
|
||||
QCOMPARE(leftSpy.count(), 1);
|
||||
QCOMPARE(leftSpy.first().first().value<Output*>(), clientOutput.data());
|
||||
QCOMPARE(s->outputs(), QVector<Output*>());
|
||||
|
||||
// leave again should not trigger
|
||||
serverSurface->setOutputs(QVector<OutputInterface*>());
|
||||
|
||||
// and enter again, just to verify
|
||||
serverSurface->setOutputs(QVector<OutputInterface*>{serverOutput});
|
||||
QCOMPARE(serverSurface->outputs(), QVector<OutputInterface*>{serverOutput});
|
||||
QVERIFY(enteredSpy.wait());
|
||||
QCOMPARE(enteredSpy.count(), 2);
|
||||
QCOMPARE(leftSpy.count(), 1);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestWaylandSurface)
|
||||
#include "test_wayland_surface.moc"
|
||||
|
|
|
@ -506,6 +506,18 @@ bool OutputInterface::isDpmsSupported() const
|
|||
return d->dpms.supported;
|
||||
}
|
||||
|
||||
QVector<wl_resource *> OutputInterface::clientResources(ClientConnection *client) const
|
||||
{
|
||||
Q_D();
|
||||
QVector<wl_resource *> ret;
|
||||
for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) {
|
||||
if (wl_resource_get_client((*it).resource) == client->client()) {
|
||||
ret << (*it).resource;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
OutputInterface *OutputInterface::get(wl_resource* native)
|
||||
{
|
||||
return Private::get(native);
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace KWayland
|
|||
namespace Server
|
||||
{
|
||||
|
||||
class ClientConnection;
|
||||
class Display;
|
||||
|
||||
/**
|
||||
|
@ -125,6 +126,12 @@ public:
|
|||
**/
|
||||
void setDpmsMode(DpmsMode mode);
|
||||
|
||||
/**
|
||||
* @returns all wl_resources bound for the @p client
|
||||
* @since 5.27
|
||||
**/
|
||||
QVector<wl_resource *> clientResources(ClientConnection *client) const;
|
||||
|
||||
static OutputInterface *get(wl_resource *native);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
|
|
@ -688,6 +688,44 @@ void SurfaceInterface::resetTrackedDamage()
|
|||
d->trackedDamage = QRegion();
|
||||
}
|
||||
|
||||
QVector<OutputInterface *> SurfaceInterface::outputs() const
|
||||
{
|
||||
Q_D();
|
||||
return d->outputs;
|
||||
}
|
||||
|
||||
void SurfaceInterface::setOutputs(const QVector<OutputInterface *> &outputs)
|
||||
{
|
||||
Q_D();
|
||||
QVector<OutputInterface *> removedOutputs = d->outputs;
|
||||
for (auto it = outputs.constBegin(), end = outputs.constEnd(); it != end; ++it) {
|
||||
const auto o = *it;
|
||||
removedOutputs.removeOne(o);
|
||||
}
|
||||
for (auto it = removedOutputs.constBegin(), end = removedOutputs.constEnd(); it != end; ++it) {
|
||||
const auto resources = (*it)->clientResources(client());
|
||||
for (wl_resource *r : resources) {
|
||||
wl_surface_send_leave(d->resource, r);
|
||||
}
|
||||
}
|
||||
// TODO: send leave when OutputInterface gets destroyed
|
||||
|
||||
QVector<OutputInterface *> addedOutputsOutputs = outputs;
|
||||
for (auto it = d->outputs.constBegin(), end = d->outputs.constEnd(); it != end; ++it) {
|
||||
const auto o = *it;
|
||||
addedOutputsOutputs.removeOne(o);
|
||||
}
|
||||
for (auto it = addedOutputsOutputs.constBegin(), end = addedOutputsOutputs.constEnd(); it != end; ++it) {
|
||||
const auto resources = (*it)->clientResources(client());
|
||||
for (wl_resource *r : resources) {
|
||||
wl_surface_send_enter(d->resource, r);
|
||||
}
|
||||
}
|
||||
// TODO: send enter when the client binds the OutputInterface another time
|
||||
|
||||
d->outputs = outputs;
|
||||
}
|
||||
|
||||
SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position)
|
||||
{
|
||||
if (!isMapped()) {
|
||||
|
|
|
@ -205,6 +205,24 @@ public:
|
|||
**/
|
||||
SurfaceInterface *surfaceAt(const QPointF &position);
|
||||
|
||||
/**
|
||||
* Sets the @p outputs this SurfaceInterface overlaps with, may be empty.
|
||||
*
|
||||
* The compositor should update whenever the SurfaceInterface becomes visible on
|
||||
* an OutputInterface by e.g. getting (un)mapped, resized, moved, etc.
|
||||
*
|
||||
* @see outputs
|
||||
* @since 5.27
|
||||
**/
|
||||
void setOutputs(const QVector<OutputInterface *> &outputs);
|
||||
|
||||
/**
|
||||
* @returns All OutputInterfaces the SurfaceInterface is on.
|
||||
* @see setOutputs
|
||||
* @since 5.27
|
||||
**/
|
||||
QVector<OutputInterface *> outputs() const;
|
||||
|
||||
/**
|
||||
* @returns The SurfaceInterface for the @p native resource.
|
||||
**/
|
||||
|
|
|
@ -89,6 +89,8 @@ public:
|
|||
// waiting on the frame callback of the never visible surface
|
||||
bool subSurfaceIsMapped = true;
|
||||
|
||||
QVector<OutputInterface *> outputs;
|
||||
|
||||
private:
|
||||
SurfaceInterface *q_func() {
|
||||
return reinterpret_cast<SurfaceInterface *>(q);
|
||||
|
|
Loading…
Reference in a new issue