diff --git a/autotests/client/test_plasmashell.cpp b/autotests/client/test_plasmashell.cpp index 75be67b..d741ad5 100644 --- a/autotests/client/test_plasmashell.cpp +++ b/autotests/client/test_plasmashell.cpp @@ -1,415 +1,470 @@ /******************************************************************** Copyright 2016 Martin Gräßlin 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 . *********************************************************************/ // Qt #include // KWayland #include "../../src/client/connection_thread.h" #include "../../src/client/compositor.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/plasmashell.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/plasmashell_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class TestPlasmaShell : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testRole_data(); void testRole(); void testPosition(); void testSkipTaskbar(); void testPanelBehavior_data(); void testPanelBehavior(); + void testAutoHidePanel(); void testDisconnect(); void testWhileDestroying(); private: Display *m_display = nullptr; CompositorInterface *m_compositorInterface = nullptr; PlasmaShellInterface *m_plasmaShellInterface = nullptr; ConnectionThread *m_connection = nullptr; Compositor *m_compositor = nullptr; EventQueue *m_queue = nullptr; QThread *m_thread = nullptr; Registry *m_registry = nullptr; PlasmaShell *m_plasmaShell = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-plasma-shell-0"); void TestPlasmaShell::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); m_display->createShm(); m_plasmaShellInterface = m_display->createPlasmaShell(m_display); m_plasmaShellInterface->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); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); QSignalSpy interfacesAnnouncedSpy(m_registry, &Registry::interfaceAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection); QVERIFY(m_registry->isValid()); m_registry->setup(); QVERIFY(interfacesAnnouncedSpy.wait()); #define CREATE(variable, factory, iface) \ variable = m_registry->create##factory(m_registry->interface(Registry::Interface::iface).name, m_registry->interface(Registry::Interface::iface).version, this); \ QVERIFY(variable); CREATE(m_compositor, Compositor, Compositor) CREATE(m_plasmaShell, PlasmaShell, PlasmaShell) #undef CREATE } void TestPlasmaShell::cleanup() { #define DELETE(name) \ if (name) { \ delete name; \ name = nullptr; \ } DELETE(m_plasmaShell) DELETE(m_compositor) DELETE(m_queue) DELETE(m_registry) #undef DELETE if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestPlasmaShell::testRole_data() { QTest::addColumn("clientRole"); QTest::addColumn("serverRole"); QTest::newRow("desktop") << PlasmaShellSurface::Role::Desktop << PlasmaShellSurfaceInterface::Role::Desktop; QTest::newRow("osd") << PlasmaShellSurface::Role::OnScreenDisplay << PlasmaShellSurfaceInterface::Role::OnScreenDisplay; QTest::newRow("panel") << PlasmaShellSurface::Role::Panel << PlasmaShellSurfaceInterface::Role::Panel; QTest::newRow("notification") << PlasmaShellSurface::Role::Notification << PlasmaShellSurfaceInterface::Role::Notification; QTest::newRow("tooltip") << PlasmaShellSurface::Role::ToolTip << PlasmaShellSurfaceInterface::Role::ToolTip; } void TestPlasmaShell::testRole() { // this test verifies that setting the role on a plasma shell surface works // first create signal spies QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); // create the surface QScopedPointer s(m_compositor->createSurface()); // no PlasmaShellSurface for the Surface yet yet QVERIFY(!PlasmaShellSurface::get(s.data())); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QCOMPARE(ps->role(), PlasmaShellSurface::Role::Normal); // now we should have a PlasmaShellSurface for QCOMPARE(PlasmaShellSurface::get(s.data()), ps.data()); // try to create another PlasmaShellSurface for the same Surface, should return from cache QCOMPARE(m_plasmaShell->createSurface(s.data()), ps.data()); // and get them on the server QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); QCOMPARE(surfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); QCOMPARE(sps->surface(), surfaceCreatedSpy.first().first().value()); QCOMPARE(sps->shell(), m_plasmaShellInterface); QCOMPARE(PlasmaShellSurfaceInterface::get(sps->resource()), sps); QVERIFY(!PlasmaShellSurfaceInterface::get(nullptr)); // default role should be normal QCOMPARE(sps->role(), PlasmaShellSurfaceInterface::Role::Normal); // now change it QSignalSpy roleChangedSpy(sps, &PlasmaShellSurfaceInterface::roleChanged); QVERIFY(roleChangedSpy.isValid()); QFETCH(PlasmaShellSurface::Role, clientRole); ps->setRole(clientRole); QCOMPARE(ps->role(), clientRole); QVERIFY(roleChangedSpy.wait()); QCOMPARE(roleChangedSpy.count(), 1); QTEST(sps->role(), "serverRole"); // try changing again should not emit the signal ps->setRole(clientRole); QVERIFY(!roleChangedSpy.wait(100)); // set role back to normal ps->setRole(PlasmaShellSurface::Role::Normal); QCOMPARE(ps->role(), PlasmaShellSurface::Role::Normal); QVERIFY(roleChangedSpy.wait()); QCOMPARE(roleChangedSpy.count(), 2); QCOMPARE(sps->role(), PlasmaShellSurfaceInterface::Role::Normal); } void TestPlasmaShell::testPosition() { // this test verifies that updating the position of a PlasmaShellSurface is properly passed to the server QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); // default position should not be set QVERIFY(!sps->isPositionSet()); QCOMPARE(sps->position(), QPoint()); // now let's try to change the position QSignalSpy positionChangedSpy(sps, &PlasmaShellSurfaceInterface::positionChanged); QVERIFY(positionChangedSpy.isValid()); ps->setPosition(QPoint(1, 2)); QVERIFY(positionChangedSpy.wait()); QCOMPARE(positionChangedSpy.count(), 1); QVERIFY(sps->isPositionSet()); QCOMPARE(sps->position(), QPoint(1, 2)); // let's try to set same position, should not trigger an update ps->setPosition(QPoint(1, 2)); QVERIFY(!positionChangedSpy.wait(100)); // different point should work, though ps->setPosition(QPoint(3, 4)); QVERIFY(positionChangedSpy.wait()); QCOMPARE(positionChangedSpy.count(), 2); QCOMPARE(sps->position(), QPoint(3, 4)); } void TestPlasmaShell::testSkipTaskbar() { // this test verifies that sip taskbar is properly passed to server QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); QVERIFY(!sps->skipTaskbar()); // now change QSignalSpy skipTaskbarChangedSpy(sps, &PlasmaShellSurfaceInterface::skipTaskbarChanged); QVERIFY(skipTaskbarChangedSpy.isValid()); ps->setSkipTaskbar(true); QVERIFY(skipTaskbarChangedSpy.wait()); QVERIFY(sps->skipTaskbar()); // setting to same again should not emit the signal ps->setSkipTaskbar(true); QEXPECT_FAIL("", "Should not be emitted if not changed", Continue); QVERIFY(!skipTaskbarChangedSpy.wait(100)); QVERIFY(sps->skipTaskbar()); // setting to false should change again ps->setSkipTaskbar(false); QVERIFY(skipTaskbarChangedSpy.wait()); QVERIFY(!sps->skipTaskbar()); } void TestPlasmaShell::testPanelBehavior_data() { QTest::addColumn("client"); QTest::addColumn("server"); QTest::newRow("autohide") << PlasmaShellSurface::PanelBehavior::AutoHide << PlasmaShellSurfaceInterface::PanelBehavior::AutoHide; QTest::newRow("can cover") << PlasmaShellSurface::PanelBehavior::WindowsCanCover << PlasmaShellSurfaceInterface::PanelBehavior::WindowsCanCover; QTest::newRow("go below") << PlasmaShellSurface::PanelBehavior::WindowsGoBelow << PlasmaShellSurfaceInterface::PanelBehavior::WindowsGoBelow; } void TestPlasmaShell::testPanelBehavior() { // this test verifies that the panel behavior is properly passed to the server QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); ps->setRole(PlasmaShellSurface::Role::Panel); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); QCOMPARE(sps->panelBehavior(), PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible); // now change the behavior QSignalSpy behaviorChangedSpy(sps, &PlasmaShellSurfaceInterface::panelBehaviorChanged); QVERIFY(behaviorChangedSpy.isValid()); QFETCH(PlasmaShellSurface::PanelBehavior, client); ps->setPanelBehavior(client); QVERIFY(behaviorChangedSpy.wait()); QTEST(sps->panelBehavior(), "server"); // changing to same should not trigger the signal ps->setPanelBehavior(client); QVERIFY(!behaviorChangedSpy.wait(100)); // but changing back to Always Visible should work ps->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); QVERIFY(behaviorChangedSpy.wait()); QCOMPARE(sps->panelBehavior(), PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible); } +void TestPlasmaShell::testAutoHidePanel() +{ + // this test verifies that auto-hiding panels work correctly + QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); + QVERIFY(plasmaSurfaceCreatedSpy.isValid()); + + QScopedPointer s(m_compositor->createSurface()); + QScopedPointer ps(m_plasmaShell->createSurface(s.data())); + ps->setRole(PlasmaShellSurface::Role::Panel); + ps->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); + QVERIFY(plasmaSurfaceCreatedSpy.wait()); + QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); + auto sps = plasmaSurfaceCreatedSpy.first().first().value(); + QVERIFY(sps); + QCOMPARE(sps->panelBehavior(), PlasmaShellSurfaceInterface::PanelBehavior::AutoHide); + + QSignalSpy autoHideRequestedSpy(sps, &PlasmaShellSurfaceInterface::panelAutoHideHideRequested); + QVERIFY(autoHideRequestedSpy.isValid()); + QSignalSpy autoHideShowRequestedSpy(sps, &PlasmaShellSurfaceInterface::panelAutoHideShowRequested); + QVERIFY(autoHideShowRequestedSpy.isValid()); + ps->requestHideAutoHidingPanel(); + QVERIFY(autoHideRequestedSpy.wait()); + QCOMPARE(autoHideRequestedSpy.count(), 1); + QCOMPARE(autoHideShowRequestedSpy.count(), 0); + + QSignalSpy panelShownSpy(ps.data(), &PlasmaShellSurface::autoHidePanelShown); + QVERIFY(panelShownSpy.isValid()); + QSignalSpy panelHiddenSpy(ps.data(), &PlasmaShellSurface::autoHidePanelHidden); + QVERIFY(panelHiddenSpy.isValid()); + + sps->hideAutoHidingPanel(); + QVERIFY(panelHiddenSpy.wait()); + QCOMPARE(panelHiddenSpy.count(), 1); + QCOMPARE(panelShownSpy.count(), 0); + + ps->requestShowAutoHidingPanel(); + QVERIFY(autoHideShowRequestedSpy.wait()); + QCOMPARE(autoHideRequestedSpy.count(), 1); + QCOMPARE(autoHideShowRequestedSpy.count(), 1); + + sps->showAutoHidingPanel(); + QVERIFY(panelShownSpy.wait()); + QCOMPARE(panelHiddenSpy.count(), 1); + QCOMPARE(panelShownSpy.count(), 1); + + // change panel type + ps->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); + // requesting auto hide should raise error + QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred); + QVERIFY(errorSpy.isValid()); + ps->requestHideAutoHidingPanel(); + QVERIFY(errorSpy.wait()); +} + void TestPlasmaShell::testDisconnect() { // this test verifies that a disconnect cleans up QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); // create the surface QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); // and get them on the server QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); // disconnect QSignalSpy clientDisconnectedSpy(sps->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); QSignalSpy surfaceDestroyedSpy(sps, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); QCOMPARE(surfaceDestroyedSpy.count(), 0); QVERIFY(surfaceDestroyedSpy.wait()); QCOMPARE(surfaceDestroyedSpy.count(), 1); s->destroy(); ps->destroy(); m_plasmaShell->destroy(); m_compositor->destroy(); m_registry->destroy(); m_queue->destroy(); } void TestPlasmaShell::testWhileDestroying() { // this test tries to hit a condition that a Surface gets created with an ID which was already // used for a previous Surface. For each Surface we try to create a PlasmaShellSurface. // Even if there was a Surface in the past with the same ID, it should create the PlasmaShellSurface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // create ShellSurface QSignalSpy shellSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(shellSurfaceCreatedSpy.isValid()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); // now try to create more surfaces QSignalSpy clientErrorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(clientErrorSpy.isValid()); for (int i = 0; i < 100; i++) { s.reset(); s.reset(m_compositor->createSurface()); m_plasmaShell->createSurface(s.data(), this); QVERIFY(surfaceCreatedSpy.wait()); } QVERIFY(clientErrorSpy.isEmpty()); QVERIFY(!clientErrorSpy.wait(100)); QVERIFY(clientErrorSpy.isEmpty()); } QTEST_GUILESS_MAIN(TestPlasmaShell) #include "test_plasmashell.moc" diff --git a/src/client/plasmashell.cpp b/src/client/plasmashell.cpp index 581aa5a..9c98e72 100644 --- a/src/client/plasmashell.cpp +++ b/src/client/plasmashell.cpp @@ -1,302 +1,336 @@ /******************************************************************** Copyright 2015 Martin Gräßlin 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 . *********************************************************************/ #include "plasmashell.h" #include "event_queue.h" #include "output.h" #include "surface.h" #include "wayland_pointer_p.h" // Wayland #include namespace KWayland { namespace Client { class PlasmaShell::Private { public: WaylandPointer shell; EventQueue *queue = nullptr; }; class PlasmaShellSurface::Private { public: Private(PlasmaShellSurface *q); ~Private(); void setup(org_kde_plasma_surface *surface); WaylandPointer surface; QSize size; QPointer parentSurface; PlasmaShellSurface::Role role; static PlasmaShellSurface *get(Surface *surface); private: + static void autoHidingPanelHiddenCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface); + static void autoHidingPanelShownCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface); + PlasmaShellSurface *q; static QVector s_surfaces; + static const org_kde_plasma_surface_listener s_listener; }; QVector PlasmaShellSurface::Private::s_surfaces; PlasmaShell::PlasmaShell(QObject *parent) : QObject(parent) , d(new Private) { } PlasmaShell::~PlasmaShell() { release(); } void PlasmaShell::destroy() { if (!d->shell) { return; } emit interfaceAboutToBeDestroyed(); d->shell.destroy(); } void PlasmaShell::release() { if (!d->shell) { return; } emit interfaceAboutToBeReleased(); d->shell.release(); } void PlasmaShell::setup(org_kde_plasma_shell *shell) { Q_ASSERT(!d->shell); Q_ASSERT(shell); d->shell.setup(shell); } void PlasmaShell::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PlasmaShell::eventQueue() { return d->queue; } PlasmaShellSurface *PlasmaShell::createSurface(wl_surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto kwS = Surface::get(surface); if (kwS) { if (auto s = PlasmaShellSurface::Private::get(kwS)) { return s; } } PlasmaShellSurface *s = new PlasmaShellSurface(parent); connect(this, &PlasmaShell::interfaceAboutToBeReleased, s, &PlasmaShellSurface::release); connect(this, &PlasmaShell::interfaceAboutToBeDestroyed, s, &PlasmaShellSurface::destroy); auto w = org_kde_plasma_shell_get_surface(d->shell, surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); s->d->parentSurface = QPointer(kwS); return s; } PlasmaShellSurface *PlasmaShell::createSurface(Surface *surface, QObject *parent) { return createSurface(*surface, parent); } bool PlasmaShell::isValid() const { return d->shell.isValid(); } PlasmaShell::operator org_kde_plasma_shell*() { return d->shell; } PlasmaShell::operator org_kde_plasma_shell*() const { return d->shell; } PlasmaShellSurface::Private::Private(PlasmaShellSurface *q) : role(PlasmaShellSurface::Role::Normal), q(q) { s_surfaces << this; } PlasmaShellSurface::Private::~Private() { s_surfaces.removeAll(this); } PlasmaShellSurface *PlasmaShellSurface::Private::get(Surface *surface) { if (!surface) { return nullptr; } for (auto it = s_surfaces.constBegin(); it != s_surfaces.constEnd(); ++it) { if ((*it)->parentSurface == surface) { return (*it)->q; } } return nullptr; } void PlasmaShellSurface::Private::setup(org_kde_plasma_surface *s) { Q_ASSERT(s); Q_ASSERT(!surface); surface.setup(s); + org_kde_plasma_surface_add_listener(surface, &s_listener, this); +} + +const org_kde_plasma_surface_listener PlasmaShellSurface::Private::s_listener = { + autoHidingPanelHiddenCallback, + autoHidingPanelShownCallback +}; + +void PlasmaShellSurface::Private::autoHidingPanelHiddenCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->surface == org_kde_plasma_surface); + emit p->q->autoHidePanelHidden(); +} + +void PlasmaShellSurface::Private::autoHidingPanelShownCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->surface == org_kde_plasma_surface); + emit p->q->autoHidePanelShown(); } PlasmaShellSurface::PlasmaShellSurface(QObject *parent) : QObject(parent) , d(new Private(this)) { } PlasmaShellSurface::~PlasmaShellSurface() { release(); } void PlasmaShellSurface::release() { d->surface.release(); } void PlasmaShellSurface::destroy() { d->surface.destroy(); } void PlasmaShellSurface::setup(org_kde_plasma_surface *surface) { d->setup(surface); } PlasmaShellSurface *PlasmaShellSurface::get(Surface *surface) { if (auto s = PlasmaShellSurface::Private::get(surface)) { return s; } return nullptr; } bool PlasmaShellSurface::isValid() const { return d->surface.isValid(); } PlasmaShellSurface::operator org_kde_plasma_surface*() { return d->surface; } PlasmaShellSurface::operator org_kde_plasma_surface*() const { return d->surface; } void PlasmaShellSurface::setPosition(const QPoint& point) { Q_ASSERT(isValid()); org_kde_plasma_surface_set_position(d->surface, point.x(), point.y()); } void PlasmaShellSurface::setRole(PlasmaShellSurface::Role role) { Q_ASSERT(isValid()); uint32_t wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_NORMAL; switch (role) { case Role::Normal: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_NORMAL; break; case Role::Desktop: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_DESKTOP; break; case Role::Panel: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_PANEL; break; case Role::OnScreenDisplay: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_ONSCREENDISPLAY; break; case Role::Notification: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_NOTIFICATION; break; case Role::ToolTip: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_TOOLTIP; break; default: Q_UNREACHABLE(); break; } org_kde_plasma_surface_set_role(d->surface, wlRole); d->role = role; } PlasmaShellSurface::Role PlasmaShellSurface::role() const { return d->role; } void PlasmaShellSurface::setPanelBehavior(PlasmaShellSurface::PanelBehavior behavior) { Q_ASSERT(isValid()); uint32_t wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_ALWAYS_VISIBLE; switch (behavior) { case PanelBehavior::AlwaysVisible: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_ALWAYS_VISIBLE; break; case PanelBehavior::AutoHide: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_AUTO_HIDE; break; case PanelBehavior::WindowsCanCover: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_CAN_COVER; break; case PanelBehavior::WindowsGoBelow: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_GO_BELOW; break; default: Q_UNREACHABLE(); break; } org_kde_plasma_surface_set_panel_behavior(d->surface, wlRole); } void PlasmaShellSurface::setSkipTaskbar(bool skip) { org_kde_plasma_surface_set_skip_taskbar(d->surface, skip); } +void PlasmaShellSurface::requestHideAutoHidingPanel() +{ + org_kde_plasma_surface_panel_auto_hide_hide(d->surface); +} + +void PlasmaShellSurface::requestShowAutoHidingPanel() +{ + org_kde_plasma_surface_panel_auto_hide_show(d->surface); +} + } } diff --git a/src/client/plasmashell.h b/src/client/plasmashell.h index 56d7371..41a2d75 100644 --- a/src/client/plasmashell.h +++ b/src/client/plasmashell.h @@ -1,304 +1,351 @@ /******************************************************************** Copyright 2015 Martin Gräßlin 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 . *********************************************************************/ #ifndef WAYLAND_PLASMASHELL_H #define WAYLAND_PLASMASHELL_H #include #include #include struct wl_surface; struct org_kde_plasma_shell; struct org_kde_plasma_surface; namespace KWayland { namespace Client { class EventQueue; class Surface; class PlasmaShellSurface; /** * @short Wrapper for the org_kde_plasma_shell interface. * * This class provides a convenient wrapper for the org_kde_plasma_shell interface. * It's main purpose is to create a PlasmaShellSurface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Shell interface: * @code * PlasmaShell *s = registry->createPlasmaShell(name, version); * @endcode * * This creates the PlasmaShell and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PlasmaShell *s = new PlasmaShell; * s->setup(registry->bindPlasmaShell(name, version)); * @endcode * * The PlasmaShell can be used as a drop-in replacement for any org_kde_plasma_shell * pointer as it provides matching cast operators. * * @see Registry * @see PlasmaShellSurface **/ class KWAYLANDCLIENT_EXPORT PlasmaShell : public QObject { Q_OBJECT public: explicit PlasmaShell(QObject *parent = nullptr); virtual ~PlasmaShell(); /** * @returns @c true if managing a org_kde_plasma_shell. **/ bool isValid() const; /** * Releases the org_kde_plasma_shell interface. * After the interface has been released the PlasmaShell instance is no * longer valid and can be setup with another org_kde_plasma_shell interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this PlasmaShell. * This method is supposed to be used when the connection to the Wayland * server goes away. Once the connection becomes invalid, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_shell interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, shell, &PlasmaShell::destroy); * @endcode * * Right before the data is destroyed, the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Setup this Shell to manage the @p shell. * When using Registry::createShell there is no need to call this * method. **/ void setup(org_kde_plasma_shell *shell); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); /** * Creates a PlasmaShellSurface for the given @p surface and sets it up. * * If a PlasmaShellSurface for the given @p surface has already been created * a pointer to the existing one is returned instead of creating a new surface. * * @param surface The native surface to create the PlasmaShellSurface for * @param parent The parent to use for the PlasmaShellSurface * @returns created PlasmaShellSurface **/ PlasmaShellSurface *createSurface(wl_surface *surface, QObject *parent = nullptr); /** * Creates a PlasmaShellSurface for the given @p surface and sets it up. * * If a PlasmaShellSurface for the given @p surface has already been created * a pointer to the existing one is returned instead of creating a new surface. * * @param surface The Surface to create the PlasmaShellSurface for * @param parent The parent to use for the PlasmaShellSurface * @returns created PlasmaShellSurface **/ PlasmaShellSurface *createSurface(Surface *surface, QObject *parent = nullptr); operator org_kde_plasma_shell*(); operator org_kde_plasma_shell*() const; Q_SIGNALS: /** * This signal is emitted right before the interface is released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createPlasmaShell * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_plasma_surface interface. * * This class is a convenient wrapper for the org_kde_plasma_surface interface. * * To create an instance use PlasmaShell::createSurface. * * A PlasmaShellSurface is a privileged Surface which can add further hints to the * Wayland server about it's position and the usage role. The Wayland server is allowed * to ignore all requests. * * Even if a PlasmaShellSurface is created for a Surface a normal ShellSurface (or similar) * needs to be created to have the Surface mapped as a window by the Wayland server. * * @see PlasmaShell * @see Surface **/ class KWAYLANDCLIENT_EXPORT PlasmaShellSurface : public QObject { Q_OBJECT public: explicit PlasmaShellSurface(QObject *parent); virtual ~PlasmaShellSurface(); /** * Releases the org_kde_plasma_surface interface. * After the interface has been released the PlasmaShellSurface instance is no * longer valid and can be setup with another org_kde_plasma_surface interface. * * This method is automatically invoked when the PlasmaShell which created this * PlasmaShellSurface gets released. **/ void release(); /** * Destroys the data held by this PlasmaShellSurface. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_surface interface * once there is a new connection available. * * This method is automatically invoked when the PlasmaShell which created this * PlasmaShellSurface gets destroyed. * * @see release **/ void destroy(); /** * Setup this PlasmaShellSurface to manage the @p surface. * There is normally no need to call this method as it's invoked by * PlasmaShell::createSurface. **/ void setup(org_kde_plasma_surface *surface); /** * @returns the PlasmaShellSurface * associated with surface, * if any, nullptr if not found. * @since 5.6 */ static PlasmaShellSurface *get(Surface *surf); /** * @returns @c true if managing a org_kde_plasma_surface. **/ bool isValid() const; operator org_kde_plasma_surface*(); operator org_kde_plasma_surface*() const; /** * Describes possible roles this PlasmaShellSurface can have. * The role can be used by the Wayland server to e.g. change the stacking order accordingly. **/ enum class Role { Normal, ///< A normal Surface Desktop, ///< The Surface represents a desktop, normally stacked below all other surfaces Panel, ///< The Surface represents a panel (dock), normally stacked above normal surfaces OnScreenDisplay, ///< The Surface represents an on screen display, like a volume changed notification Notification, ///< The Surface represents a notification @since 5.24 ToolTip ///< The Surface represents a tooltip @since 5.24 }; /** * Changes the requested Role to @p role. * @see role **/ void setRole(Role role); /** * @returns The requested Role, default value is @c Role::Normal. * @see setRole **/ Role role() const; /** * Requests to position this PlasmaShellSurface at @p point in global coordinates. **/ void setPosition(const QPoint &point); /** * Describes how a PlasmaShellSurface with role @c Role::Panel should behave. * @see Role **/ enum class PanelBehavior { AlwaysVisible, AutoHide, WindowsCanCover, WindowsGoBelow }; /** * Sets the PanelBehavior for a PlasmaShellSurface with Role @c Role::Panel * @see setRole **/ void setPanelBehavior(PanelBehavior behavior); /** * Setting this bit to the window, will make it say it prefers * to not be listed in the taskbar. Taskbar implementations * may or may not follow this hint. * @since 5.5 */ void setSkipTaskbar(bool skip); + /** + * Requests to hide a surface with Role Panel and PanelBahvior AutoHide. + * + * Once the compositor has hidden the panel the signal @link{autoHidePanelHidden} gets + * emitted. Once it is shown again the signal @link{autoHidePanelShown} gets emitted. + * + * To show the surface again from client side use @link{requestShowAutoHidingPanel}. + * + * @see autoHidePanelHidden + * @see autoHidePanelShown + * @see requestShowAutoHidingPanel + * @since 5.28 + **/ + void requestHideAutoHidingPanel(); + + /** + * Requests to show a surface with Role Panel and PanelBahvior AutoHide. + * + * This request allows the client to show a surface which it previously + * requested to be hidden with @link{requestHideAutoHidingPanel}. + * + * @see autoHidePanelHidden + * @see autoHidePanelShown + * @see requestHideAutoHidingPanel + * @since 5.28 + **/ + void requestShowAutoHidingPanel(); + +Q_SIGNALS: + /** + * Emitted when the compositor hided an auto hiding panel. + * @see requestHideAutoHidingPanel + * @see autoHidePanelShown + * @see requestShowAutoHidingPanel + * @since 5.28 + **/ + void autoHidePanelHidden(); + + /** + * Emitted when the compositor showed an auto hiding panel. + * @see requestHideAutoHidingPanel + * @see autoHidePanelHidden + * @see requestShowAutoHidingPanel + * @since 5.28 + **/ + void autoHidePanelShown(); + private: friend class PlasmaShell; class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::PlasmaShellSurface::Role) Q_DECLARE_METATYPE(KWayland::Client::PlasmaShellSurface::PanelBehavior) #endif diff --git a/src/client/protocols/plasma-shell.xml b/src/client/protocols/plasma-shell.xml index 8c9b5a5..db94067 100644 --- a/src/client/protocols/plasma-shell.xml +++ b/src/client/protocols/plasma-shell.xml @@ -1,357 +1,377 @@ . ]]> - + This interface is used by KF5 powered Wayland shells to communicate with the compositor and can only be bound one time. Create a shell surface for an existing surface. Only one shell surface can be associated with a given surface. - + An interface that may be implemented by a wl_surface, for implementations that provide the shell user interface. It provides requests to set surface roles, assign an output or set the position in output coordinates. On the server side the object is automatically destroyed when the related wl_surface is destroyed. On client side, org_kde_plasma_surface.destroy() must be called before destroying the wl_surface object. The org_kde_plasma_surface interface is removed from the wl_surface object that was turned into a shell surface with the org_kde_plasma_shell.get_surface request. The shell surface role is lost and wl_surface is unmapped. Assign an output to this shell surface. The compositor will use this information to set the position when org_kde_plasma_surface.set_position request is called. Move the surface to new coordinates. Coordinates are global, for example 50,50 for a 1920,0+1920x1080 output is 1970,50 in global coordinates space. Use org_kde_plasma_surface.set_output to assign an output to this surface. Assign a role to a shell surface. The compositor handles surfaces depending on their role. See the explaination below. This request fails if the surface already has a role, this means the surface role may be assigned only once. == Surfaces with splash role == Splash surfaces are placed above every other surface during the shell startup phase. The surfaces are placed according to the output coordinates. No size is imposed to those surfaces, the shell has to resize them according to output size. These surfaces are meant to hide the desktop during the startup phase so that the user will always see a ready to work desktop. A shell might not create splash surfaces if the compositor reveals the desktop in an alternative fashion, for example with a fade in effect. That depends on how much time the desktop usually need to prepare the workspace or specific design decisions. This specification doesn't impose any particular design. When the startup phase is finished, the shell will send the org_kde_plasma.desktop_ready request to the compositor. == Surfaces with desktop role == Desktop surfaces are placed below all other surfaces and are used to show the actual desktop view with icons, search results or controls the user will interact with. What to show depends on the shell implementation. The surfaces are placed according to the output coordinates. No size is imposed to those surfaces, the shell has to resize them according to output size. Only one surface per output can have the desktop role. == Surfaces with dashboard role == Dashboard surfaces are placed above desktop surfaces and are used to show additional widgets and controls. The surfaces are placed according to the output coordinates. No size is imposed to those surfaces, the shell has to resize them according to output size. Only one surface per output can have the dashboard role. == Surfaces with config role == A configuration surface is shown when the user wants to configure panel or desktop views. Only one surface per output can have the config role. TODO: This should grab the input like popup menus, right? == Surfaces with overlay role == Overlays are special surfaces that shows for a limited amount of time. Such surfaces are useful to display things like volume, brightness and status changes. Compositors may decide to show those surfaces in a layer above all surfaces, even full screen ones if so is desired. == Surfaces with notification role == Notification surfaces display informative content for a limited amount of time. The compositor may decide to show them in a corner depending on the configuration. These surfaces are shown in a layer above all other surfaces except for full screen ones. == Surfaces with lock role == The lock surface is shown by the compositor when the session is locked, users interact with it to unlock the session. Compositors should move lock surfaces to 0,0 in output coordinates space and hide all other surfaces for security sake. For the same reason it is recommended that clients make the lock surface as big as the screen. Only one surface per output can have the lock role. The panel is on top of other surfaces, windows cannot cover (full screen windows excluded). The panel is hidden automatically and restored when the mouse is over. Windows can cover the panel. Maximized windows take the whole screen space but the panel is above the windows. Set flags bitmask as described by the flag enum. Pass 0 to unset any flag, the surface will adjust its behavior to the default. Setting this bit to the window, will make it say it prefers to not be listed in the taskbar. Taskbar implementations may or may not follow this hint. - - - - + + + + A panel surface with panel_behavior auto_hide can perform this request to show the panel + again which got hidden with panel_auto_hide_hide. + + + + + + An auto-hiding panel got hidden by the compositor. + + + + + + An auto-hiding panel got shown by the compositor. + + diff --git a/src/client/registry.cpp b/src/client/registry.cpp index 11dda50..59f384f 100644 --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -1,700 +1,700 @@ /******************************************************************** Copyright 2014 Martin Gräßlin 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 . *********************************************************************/ #include "registry.h" #include "compositor.h" #include "connection_thread.h" #include "datadevicemanager.h" #include "dpms.h" #include "event_queue.h" #include "fakeinput.h" #include "fullscreen_shell.h" #include "idle.h" #include "logging_p.h" #include "outputconfiguration.h" #include "outputmanagement.h" #include "outputdevice.h" #include "output.h" #include "plasmashell.h" #include "plasmawindowmanagement.h" #include "seat.h" #include "shadow.h" #include "blur.h" #include "contrast.h" #include "relativepointer.h" #include "server_decoration.h" #include "slide.h" #include "shell.h" #include "shm_pool.h" #include "subcompositor.h" #include "textinput_p.h" #include "xdgshell.h" #include "xdgshell_p.h" #include "wayland_pointer_p.h" // Qt #include // wayland #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /***** * How to add another interface: * * define a new enum value in Registry::Interface * * define the bind method * * define the create method * * define the Announced signal * * define the Removed signal * * add a block to s_interfaces * * add the BIND macro for the new bind * * add the CREATE macro for the new create * * extend registry unit test to verify that it works ****/ namespace KWayland { namespace Client { namespace { struct SuppertedInterfaceData { quint32 maxVersion; QByteArray name; const wl_interface *interface; void (Registry::*announcedSignal)(quint32, quint32); void (Registry::*removedSignal)(quint32); }; static const QMap s_interfaces = { {Registry::Interface::Compositor, { 3, QByteArrayLiteral("wl_compositor"), &wl_compositor_interface, &Registry::compositorAnnounced, &Registry::compositorRemoved }}, {Registry::Interface::DataDeviceManager, { 2, QByteArrayLiteral("wl_data_device_manager"), &wl_data_device_manager_interface, &Registry::dataDeviceManagerAnnounced, &Registry::dataDeviceManagerRemoved }}, {Registry::Interface::Output, { 2, QByteArrayLiteral("wl_output"), &wl_output_interface, &Registry::outputAnnounced, &Registry::outputRemoved }}, {Registry::Interface::Shm, { 1, QByteArrayLiteral("wl_shm"), &wl_shm_interface, &Registry::shmAnnounced, &Registry::shmRemoved }}, {Registry::Interface::Seat, { 4, QByteArrayLiteral("wl_seat"), &wl_seat_interface, &Registry::seatAnnounced, &Registry::seatRemoved }}, {Registry::Interface::Shell, { 1, QByteArrayLiteral("wl_shell"), &wl_shell_interface, &Registry::shellAnnounced, &Registry::shellRemoved }}, {Registry::Interface::SubCompositor, { 1, QByteArrayLiteral("wl_subcompositor"), &wl_subcompositor_interface, &Registry::subCompositorAnnounced, &Registry::subCompositorRemoved }}, {Registry::Interface::PlasmaShell, { - 3, + 4, QByteArrayLiteral("org_kde_plasma_shell"), &org_kde_plasma_shell_interface, &Registry::plasmaShellAnnounced, &Registry::plasmaShellRemoved }}, {Registry::Interface::PlasmaWindowManagement, { 7, QByteArrayLiteral("org_kde_plasma_window_management"), &org_kde_plasma_window_management_interface, &Registry::plasmaWindowManagementAnnounced, &Registry::plasmaWindowManagementRemoved }}, {Registry::Interface::Idle, { 1, QByteArrayLiteral("org_kde_kwin_idle"), &org_kde_kwin_idle_interface, &Registry::idleAnnounced, &Registry::idleRemoved }}, {Registry::Interface::FakeInput, { 2, QByteArrayLiteral("org_kde_kwin_fake_input"), &org_kde_kwin_fake_input_interface, &Registry::fakeInputAnnounced, &Registry::fakeInputRemoved }}, {Registry::Interface::OutputManagement, { 1, QByteArrayLiteral("org_kde_kwin_outputmanagement"), &org_kde_kwin_outputmanagement_interface, &Registry::outputManagementAnnounced, &Registry::outputManagementRemoved }}, {Registry::Interface::OutputDevice, { 1, QByteArrayLiteral("org_kde_kwin_outputdevice"), &org_kde_kwin_outputdevice_interface, &Registry::outputDeviceAnnounced, &Registry::outputDeviceRemoved }}, {Registry::Interface::Shadow, { 2, QByteArrayLiteral("org_kde_kwin_shadow_manager"), &org_kde_kwin_shadow_manager_interface, &Registry::shadowAnnounced, &Registry::shadowRemoved }}, {Registry::Interface::Blur, { 1, QByteArrayLiteral("org_kde_kwin_blur_manager"), &org_kde_kwin_blur_manager_interface, &Registry::blurAnnounced, &Registry::blurRemoved }}, {Registry::Interface::Contrast, { 1, QByteArrayLiteral("org_kde_kwin_contrast_manager"), &org_kde_kwin_contrast_manager_interface, &Registry::contrastAnnounced, &Registry::contrastRemoved }}, {Registry::Interface::Slide, { 1, QByteArrayLiteral("org_kde_kwin_slide_manager"), &org_kde_kwin_slide_manager_interface, &Registry::slideAnnounced, &Registry::slideRemoved }}, {Registry::Interface::FullscreenShell, { 1, QByteArrayLiteral("_wl_fullscreen_shell"), &_wl_fullscreen_shell_interface, &Registry::fullscreenShellAnnounced, &Registry::fullscreenShellRemoved }}, {Registry::Interface::Dpms, { 1, QByteArrayLiteral("org_kde_kwin_dpms_manager"), &org_kde_kwin_dpms_manager_interface, &Registry::dpmsAnnounced, &Registry::dpmsRemoved }}, {Registry::Interface::ServerSideDecorationManager, { 1, QByteArrayLiteral("org_kde_kwin_server_decoration_manager"), &org_kde_kwin_server_decoration_manager_interface, &Registry::serverSideDecorationManagerAnnounced, &Registry::serverSideDecorationManagerRemoved }}, {Registry::Interface::TextInputManagerUnstableV0, { 1, QByteArrayLiteral("wl_text_input_manager"), &wl_text_input_manager_interface, &Registry::textInputManagerUnstableV0Announced, &Registry::textInputManagerUnstableV0Removed }}, {Registry::Interface::TextInputManagerUnstableV2, { 1, QByteArrayLiteral("zwp_text_input_manager_v2"), &zwp_text_input_manager_v2_interface, &Registry::textInputManagerUnstableV2Announced, &Registry::textInputManagerUnstableV2Removed }}, {Registry::Interface::XdgShellUnstableV5, { 1, QByteArrayLiteral("xdg_shell"), &xdg_shell_interface, &Registry::xdgShellUnstableV5Announced, &Registry::xdgShellUnstableV5Removed }}, {Registry::Interface::RelativePointerManagerUnstableV1, { 1, QByteArrayLiteral("zwp_relative_pointer_manager_v1"), &zwp_relative_pointer_manager_v1_interface, &Registry::relativePointerManagerUnstableV1Announced, &Registry::relativePointerManagerUnstableV1Removed }} }; static quint32 maxVersion(const Registry::Interface &interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().maxVersion; } return 0; } } class Registry::Private { public: Private(Registry *q); void setup(); bool hasInterface(Interface interface) const; AnnouncedInterface interface(Interface interface) const; QVector interfaces(Interface interface) const; Interface interfaceForName(quint32 name) const; template T *bind(Interface interface, uint32_t name, uint32_t version) const; template T *create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const); WaylandPointer registry; static const struct wl_callback_listener s_callbackListener; WaylandPointer callback; EventQueue *queue = nullptr; private: void handleAnnounce(uint32_t name, const char *interface, uint32_t version); void handleRemove(uint32_t name); void handleGlobalSync(); static void globalAnnounce(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void globalRemove(void *data, struct wl_registry *registry, uint32_t name); static void globalSync(void *data, struct wl_callback *callback, uint32_t serial); Registry *q; struct InterfaceData { Interface interface; uint32_t name; uint32_t version; }; QList m_interfaces; static const struct wl_registry_listener s_registryListener; }; Registry::Private::Private(Registry *q) : q(q) { } void Registry::Private::setup() { wl_registry_add_listener(registry, &s_registryListener, this); wl_callback_add_listener(callback, &s_callbackListener, this); } Registry::Registry(QObject *parent) : QObject(parent) , d(new Private(this)) { } Registry::~Registry() { release(); } void Registry::release() { d->registry.release(); d->callback.release(); } void Registry::destroy() { d->registry.destroy(); d->callback.destroy(); } void Registry::create(wl_display *display) { Q_ASSERT(display); Q_ASSERT(!isValid()); d->registry.setup(wl_display_get_registry(display)); d->callback.setup(wl_display_sync(display)); if (d->queue) { d->queue->addProxy(d->registry); d->queue->addProxy(d->callback); } } void Registry::create(ConnectionThread *connection) { create(connection->display()); } void Registry::setup() { Q_ASSERT(isValid()); d->setup(); } void Registry::setEventQueue(EventQueue *queue) { d->queue = queue; if (!queue) { return; } if (d->registry) { d->queue->addProxy(d->registry); } if (d->callback) { d->queue->addProxy(d->callback); } } EventQueue *Registry::eventQueue() { return d->queue; } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_registry_listener Registry::Private::s_registryListener = { globalAnnounce, globalRemove }; const struct wl_callback_listener Registry::Private::s_callbackListener = { globalSync }; #endif void Registry::Private::globalAnnounce(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleAnnounce(name, interface, version); } void Registry::Private::globalRemove(void *data, wl_registry *registry, uint32_t name) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleRemove(name); } void Registry::Private::globalSync(void* data, wl_callback* callback, uint32_t serial) { Q_UNUSED(serial) auto r = reinterpret_cast(data); Q_ASSERT(r->callback == callback); r->handleGlobalSync(); r->callback.destroy(); } void Registry::Private::handleGlobalSync() { emit q->interfacesAnnounced(); } namespace { static Registry::Interface nameToInterface(const char *interface) { for (auto it = s_interfaces.begin(); it != s_interfaces.end(); ++it) { if (qstrcmp(interface, it.value().name) == 0) { return it.key(); } } return Registry::Interface::Unknown; } } void Registry::Private::handleAnnounce(uint32_t name, const char *interface, uint32_t version) { Interface i = nameToInterface(interface); emit q->interfaceAnnounced(QByteArray(interface), name, version); if (i == Interface::Unknown) { qCDebug(KWAYLAND_CLIENT) << "Unknown interface announced: " << interface << "/" << name << "/" << version; return; } qCDebug(KWAYLAND_CLIENT) << "Wayland Interface: " << interface << "/" << name << "/" << version; m_interfaces.append({i, name, version}); auto it = s_interfaces.constFind(i); if (it != s_interfaces.end()) { emit (q->*it.value().announcedSignal)(name, version); } } void Registry::Private::handleRemove(uint32_t name) { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [name](const InterfaceData &data) { return data.name == name; } ); if (it != m_interfaces.end()) { InterfaceData data = *(it); m_interfaces.erase(it); auto sit = s_interfaces.find(data.interface); if (sit != s_interfaces.end()) { emit (q->*sit.value().removedSignal)(data.name); } } emit q->interfaceRemoved(name); } bool Registry::Private::hasInterface(Registry::Interface interface) const { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [interface](const InterfaceData &data) { return data.interface == interface; } ); return it != m_interfaces.end(); } QVector Registry::Private::interfaces(Interface interface) const { QVector retVal; for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { const auto &data = *it; if (data.interface == interface) { retVal << AnnouncedInterface{data.name, data.version}; } } return retVal; } Registry::AnnouncedInterface Registry::Private::interface(Interface interface) const { const auto all = interfaces(interface); if (!all.isEmpty()) { return all.last(); } return AnnouncedInterface{0, 0}; } Registry::Interface Registry::Private::interfaceForName(quint32 name) const { auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [name] (const InterfaceData &data) { return data.name == name; }); if (it == m_interfaces.constEnd()) { return Interface::Unknown; } return (*it).interface; } bool Registry::hasInterface(Registry::Interface interface) const { return d->hasInterface(interface); } QVector Registry::interfaces(Interface interface) const { return d->interfaces(interface); } Registry::AnnouncedInterface Registry::interface(Interface interface) const { return d->interface(interface); } #define BIND2(__NAME__, __INAME__, __WL__) \ __WL__ *Registry::bind##__NAME__(uint32_t name, uint32_t version) const \ { \ return d->bind<__WL__>(Interface::__INAME__, name, qMin(maxVersion(Interface::__INAME__), version)); \ } #define BIND(__NAME__, __WL__) BIND2(__NAME__, __NAME__, __WL__) BIND(Compositor, wl_compositor) BIND(Output, wl_output) BIND(Seat, wl_seat) BIND(Shell, wl_shell) BIND(Shm, wl_shm) BIND(SubCompositor, wl_subcompositor) BIND(FullscreenShell, _wl_fullscreen_shell) BIND(DataDeviceManager, wl_data_device_manager) BIND(PlasmaShell, org_kde_plasma_shell) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) BIND(FakeInput, org_kde_kwin_fake_input) BIND(OutputManagement, org_kde_kwin_outputmanagement) BIND(OutputDevice, org_kde_kwin_outputdevice) BIND(ServerSideDecorationManager, org_kde_kwin_server_decoration_manager) BIND(TextInputManagerUnstableV0, wl_text_input_manager) BIND(TextInputManagerUnstableV2, zwp_text_input_manager_v2) BIND(XdgShellUnstableV5, xdg_shell) BIND(RelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1) BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager) BIND2(BlurManager, Blur, org_kde_kwin_blur_manager) BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager) BIND2(SlideManager, Slide, org_kde_kwin_slide_manager) BIND2(DpmsManager, Dpms, org_kde_kwin_dpms_manager) #undef BIND #undef BIND2 template T *Registry::Private::create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const) { T *t = new T(parent); t->setEventQueue(queue); t->setup((q->*bindMethod)(name, version)); QObject::connect(q, &Registry::interfaceRemoved, t, [t, name] (quint32 removed) { if (name == removed) { emit t->removed(); } } ); return t; } #define CREATE2(__NAME__, __BINDNAME__) \ __NAME__ *Registry::create##__NAME__(quint32 name, quint32 version, QObject *parent) \ { \ return d->create<__NAME__>(name, version, parent, &Registry::bind##__BINDNAME__); \ } #define CREATE(__NAME__) CREATE2(__NAME__, __NAME__) CREATE(Compositor) CREATE(Seat) CREATE(Shell) CREATE(SubCompositor) CREATE(FullscreenShell) CREATE(Output) CREATE(DataDeviceManager) CREATE(PlasmaShell) CREATE(PlasmaWindowManagement) CREATE(Idle) CREATE(FakeInput) CREATE(OutputManagement) CREATE(OutputDevice) CREATE(ShadowManager) CREATE(BlurManager) CREATE(ContrastManager) CREATE(SlideManager) CREATE(DpmsManager) CREATE(ServerSideDecorationManager) CREATE2(ShmPool, Shm) #undef CREATE #undef CREATE2 TextInputManager *Registry::createTextInputManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::TextInputManagerUnstableV0: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV0); case Interface::TextInputManagerUnstableV2: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV2); default: return nullptr; } } XdgShell *Registry::createXdgShell(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::XdgShellUnstableV5: return d->create(name, version, parent, &Registry::bindXdgShellUnstableV5); default: return nullptr; } } RelativePointerManager *Registry::createRelativePointerManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::RelativePointerManagerUnstableV1: return d->create(name, version, parent, &Registry::bindRelativePointerManagerUnstableV1); default: return nullptr; } } namespace { static const wl_interface *wlInterface(Registry::Interface interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().interface; } return nullptr; } } template T *Registry::Private::bind(Registry::Interface interface, uint32_t name, uint32_t version) const { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [=](const InterfaceData &data) { return data.interface == interface && data.name == name && data.version >= version; }); if (it == m_interfaces.end()) { qCDebug(KWAYLAND_CLIENT) << "Don't have interface " << int(interface) << "with name " << name << "and minimum version" << version; return nullptr; } auto t = reinterpret_cast(wl_registry_bind(registry, name, wlInterface(interface), version)); if (queue) { queue->addProxy(t); } return t; } bool Registry::isValid() const { return d->registry.isValid(); } wl_registry *Registry::registry() { return d->registry; } Registry::operator wl_registry*() const { return d->registry; } Registry::operator wl_registry*() { return d->registry; } } } diff --git a/src/server/plasmashell_interface.cpp b/src/server/plasmashell_interface.cpp index a374286..9ad3f99 100644 --- a/src/server/plasmashell_interface.cpp +++ b/src/server/plasmashell_interface.cpp @@ -1,334 +1,378 @@ /******************************************************************** Copyright 2015 Martin Gräßlin 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 . *********************************************************************/ #include "plasmashell_interface.h" #include "global_p.h" #include "resource_p.h" #include "display.h" #include "surface_interface.h" #include #include #include namespace KWayland { namespace Server { class PlasmaShellInterface::Private : public Global::Private { public: Private(PlasmaShellInterface *q, Display *d); QList surfaces; private: static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); void bind(wl_client *client, uint32_t version, uint32_t id) override; void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); PlasmaShellInterface *q; static const struct org_kde_plasma_shell_interface s_interface; static const quint32 s_version; }; -const quint32 PlasmaShellInterface::Private::s_version = 3; +const quint32 PlasmaShellInterface::Private::s_version = 4; PlasmaShellInterface::Private::Private(PlasmaShellInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_shell_interface, s_version) , q(q) { } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_plasma_shell_interface PlasmaShellInterface::Private::s_interface = { createSurfaceCallback }; #endif class PlasmaShellSurfaceInterface::Private : public Resource::Private { public: Private(PlasmaShellSurfaceInterface *q, PlasmaShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource); SurfaceInterface *surface; QPoint m_globalPos; Role m_role = Role::Normal; bool m_positionSet = false; PanelBehavior m_panelBehavior = PanelBehavior::AlwaysVisible; bool m_skipTaskbar = false; private: // interface callbacks static void setOutputCallback(wl_client *client, wl_resource *resource, wl_resource *output); static void setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y); static void setRoleCallback(wl_client *client, wl_resource *resource, uint32_t role); static void setPanelBehaviorCallback(wl_client *client, wl_resource *resource, uint32_t flag); static void setSkipTaskbarCallback(wl_client *client, wl_resource *resource, uint32_t skip); + static void panelAutoHideHideCallback(wl_client *client, wl_resource *resource); + static void panelAutoHideShowCallback(wl_client *client, wl_resource *resource); void setPosition(const QPoint &globalPos); void setRole(uint32_t role); void setPanelBehavior(org_kde_plasma_surface_panel_behavior behavior); PlasmaShellSurfaceInterface *q_func() { return reinterpret_cast(q); } static const struct org_kde_plasma_surface_interface s_interface; }; PlasmaShellInterface::PlasmaShellInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } PlasmaShellInterface::~PlasmaShellInterface() = default; void PlasmaShellInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *shell = c->createResource(&org_kde_plasma_shell_interface, qMin(version, s_version), id); if (!shell) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(shell, &s_interface, this, nullptr); } void PlasmaShellInterface::Private::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { auto s = reinterpret_cast(wl_resource_get_user_data(resource)); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void PlasmaShellInterface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](PlasmaShellSurfaceInterface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), WL_DISPLAY_ERROR_INVALID_OBJECT, "PlasmaShellSurface already created"); return; } PlasmaShellSurfaceInterface *shellSurface = new PlasmaShellSurfaceInterface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &PlasmaShellSurfaceInterface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); emit q->surfaceCreated(shellSurface); } /********************************* * ShellSurfaceInterface *********************************/ PlasmaShellSurfaceInterface::Private::Private(PlasmaShellSurfaceInterface *q, PlasmaShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource) : Resource::Private(q, shell, parentResource, &org_kde_plasma_surface_interface, &s_interface) , surface(surface) { } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_plasma_surface_interface PlasmaShellSurfaceInterface::Private::s_interface = { resourceDestroyedCallback, setOutputCallback, setPositionCallback, setRoleCallback, setPanelBehaviorCallback, - setSkipTaskbarCallback + setSkipTaskbarCallback, + panelAutoHideHideCallback, + panelAutoHideShowCallback }; #endif PlasmaShellSurfaceInterface::PlasmaShellSurfaceInterface(PlasmaShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource) : Resource(new Private(this, shell, parent, parentResource)) { auto unsetSurface = [this] { Q_D(); d->surface = nullptr; }; connect(parent, &Resource::unbound, this, unsetSurface); connect(parent, &QObject::destroyed, this, unsetSurface); } PlasmaShellSurfaceInterface::~PlasmaShellSurfaceInterface() = default; SurfaceInterface *PlasmaShellSurfaceInterface::surface() const { Q_D(); return d->surface; } PlasmaShellInterface *PlasmaShellSurfaceInterface::shell() const { Q_D(); return reinterpret_cast(d->global); } PlasmaShellSurfaceInterface::Private *PlasmaShellSurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } void PlasmaShellSurfaceInterface::Private::setOutputCallback(wl_client *client, wl_resource *resource, wl_resource *output) { Q_UNUSED(client) Q_UNUSED(resource) Q_UNUSED(output) // TODO: implement } void PlasmaShellSurfaceInterface::Private::setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->setPosition(QPoint(x, y)); } void PlasmaShellSurfaceInterface::Private::setPosition(const QPoint &globalPos) { if (m_globalPos == globalPos && m_positionSet) { return; } m_positionSet = true; m_globalPos = globalPos; Q_Q(PlasmaShellSurfaceInterface); emit q->positionChanged(); } void PlasmaShellSurfaceInterface::Private::setRoleCallback(wl_client *client, wl_resource *resource, uint32_t role) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->setRole(role); } void PlasmaShellSurfaceInterface::Private::setRole(uint32_t role) { Role r = Role::Normal; switch (role) { case ORG_KDE_PLASMA_SURFACE_ROLE_DESKTOP: r = Role::Desktop; break; case ORG_KDE_PLASMA_SURFACE_ROLE_PANEL: r = Role::Panel; break; case ORG_KDE_PLASMA_SURFACE_ROLE_ONSCREENDISPLAY: r = Role::OnScreenDisplay; break; case ORG_KDE_PLASMA_SURFACE_ROLE_NOTIFICATION: r = Role::Notification; break; case ORG_KDE_PLASMA_SURFACE_ROLE_TOOLTIP: r = Role::ToolTip; break; case ORG_KDE_PLASMA_SURFACE_ROLE_NORMAL: default: r = Role::Normal; break; } if (r == m_role) { return; } m_role = r; Q_Q(PlasmaShellSurfaceInterface); emit q->roleChanged(); } void PlasmaShellSurfaceInterface::Private::setPanelBehaviorCallback(wl_client *client, wl_resource *resource, uint32_t flag) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->setPanelBehavior(org_kde_plasma_surface_panel_behavior(flag)); } void PlasmaShellSurfaceInterface::Private::setSkipTaskbarCallback(wl_client *client, wl_resource *resource, uint32_t skip) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_skipTaskbar = (bool)skip; emit s->q_func()->skipTaskbarChanged(); } +void PlasmaShellSurfaceInterface::Private::panelAutoHideHideCallback(wl_client *client, wl_resource *resource) +{ + auto s = cast(resource); + Q_ASSERT(client == *s->client); + if (s->m_role != Role::Panel || s->m_panelBehavior != PanelBehavior::AutoHide) { + wl_resource_post_error(s->resource, ORG_KDE_PLASMA_SURFACE_ERROR_PANEL_NOT_AUTO_HIDE, "Not an auto hide panel"); + return; + } + emit s->q_func()->panelAutoHideHideRequested(); +} + +void PlasmaShellSurfaceInterface::Private::panelAutoHideShowCallback(wl_client *client, wl_resource *resource) +{ + auto s = cast(resource); + Q_ASSERT(client == *s->client); + if (s->m_role != Role::Panel || s->m_panelBehavior != PanelBehavior::AutoHide) { + wl_resource_post_error(s->resource, ORG_KDE_PLASMA_SURFACE_ERROR_PANEL_NOT_AUTO_HIDE, "Not an auto hide panel"); + return; + } + emit s->q_func()->panelAutoHideShowRequested(); +} + void PlasmaShellSurfaceInterface::Private::setPanelBehavior(org_kde_plasma_surface_panel_behavior behavior) { PanelBehavior newBehavior = PanelBehavior::AlwaysVisible; switch (behavior) { case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_AUTO_HIDE: newBehavior = PanelBehavior::AutoHide; break; case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_CAN_COVER: newBehavior = PanelBehavior::WindowsCanCover; break; case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_GO_BELOW: newBehavior = PanelBehavior::WindowsGoBelow; break; case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_ALWAYS_VISIBLE: default: break; } if (m_panelBehavior == newBehavior) { return; } m_panelBehavior = newBehavior; Q_Q(PlasmaShellSurfaceInterface); emit q->panelBehaviorChanged(); } QPoint PlasmaShellSurfaceInterface::position() const { Q_D(); return d->m_globalPos; } PlasmaShellSurfaceInterface::Role PlasmaShellSurfaceInterface::role() const { Q_D(); return d->m_role; } bool PlasmaShellSurfaceInterface::isPositionSet() const { Q_D(); return d->m_positionSet; } PlasmaShellSurfaceInterface::PanelBehavior PlasmaShellSurfaceInterface::panelBehavior() const { Q_D(); return d->m_panelBehavior; } bool PlasmaShellSurfaceInterface::skipTaskbar() const { Q_D(); return d->m_skipTaskbar; } +void PlasmaShellSurfaceInterface::hideAutoHidingPanel() +{ + Q_D(); + if (!d->resource) { + return; + } + org_kde_plasma_surface_send_auto_hidden_panel_hidden(d->resource); +} + +void PlasmaShellSurfaceInterface::showAutoHidingPanel() +{ + Q_D(); + if (!d->resource) { + return; + } + org_kde_plasma_surface_send_auto_hidden_panel_shown(d->resource); +} + PlasmaShellSurfaceInterface *PlasmaShellSurfaceInterface::get(wl_resource *native) { return Private::get(native); } } } diff --git a/src/server/plasmashell_interface.h b/src/server/plasmashell_interface.h index 14962b3..9ab8b33 100644 --- a/src/server/plasmashell_interface.h +++ b/src/server/plasmashell_interface.h @@ -1,178 +1,228 @@ /******************************************************************** Copyright 2015 Martin Gräßlin 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 . *********************************************************************/ #ifndef WAYLAND_SERVER_PLASMA_SHELL_INTERFACE_H #define WAYLAND_SERVER_PLASMA_SHELL_INTERFACE_H #include #include #include "global.h" #include "resource.h" class QSize; struct wl_resource; namespace KWayland { namespace Server { class Display; class SurfaceInterface; class PlasmaShellSurfaceInterface; /** * @brief Global for the org_kde_plasma_shell interface. * * The PlasmaShellInterface allows to add additional information to a SurfaceInterface. * It goes beyond what a ShellSurfaceInterface provides and is adjusted toward the needs * of the Plasma desktop. * * A server providing this interface should think about how to restrict access to it as * it allows to perform absolute window positioning. * * @since 5.4 **/ class KWAYLANDSERVER_EXPORT PlasmaShellInterface : public Global { Q_OBJECT public: virtual ~PlasmaShellInterface(); Q_SIGNALS: /** * Emitted whenever a PlasmaShellSurfaceInterface got created. **/ void surfaceCreated(KWayland::Server::PlasmaShellSurfaceInterface*); private: friend class Display; explicit PlasmaShellInterface(Display *display, QObject *parent); class Private; }; /** * @brief Resource for the org_kde_plasma_shell_surface interface. * * PlasmaShellSurfaceInterface gets created by PlasmaShellInterface. * * @since 5.4 **/ class KWAYLANDSERVER_EXPORT PlasmaShellSurfaceInterface : public Resource { Q_OBJECT public: virtual ~PlasmaShellSurfaceInterface(); /** * @returns the SurfaceInterface this PlasmaShellSurfaceInterface got created for **/ SurfaceInterface *surface() const; /** * @returns The PlasmaShellInterface which created this PlasmaShellSurfaceInterface. **/ PlasmaShellInterface *shell() const; /** * @returns the requested position in global coordinates. **/ QPoint position() const; /** * @returns Whether a global position has been requested. **/ bool isPositionSet() const; /** * Describes possible roles this PlasmaShellSurfaceInterface can have. * The role can be used by the server to e.g. change the stacking order accordingly. **/ enum class Role { Normal, ///< A normal surface Desktop, ///< The surface represents a desktop, normally stacked below all other surfaces Panel, ///< The surface represents a panel (dock), normally stacked above normal surfaces OnScreenDisplay, ///< The surface represents an on screen display, like a volume changed notification Notification, ///< The surface represents a notification @since 5.24 ToolTip ///< The surface represents a tooltip @since 5.24 }; /** * @returns The requested role, default value is @c Role::Normal. **/ Role role() const; /** * Describes how a PlasmaShellSurfaceInterface with role @c Role::Panel should behave. **/ enum class PanelBehavior { AlwaysVisible, ///< The panel should be always visible AutoHide, ///< The panel auto hides at a screen edge and returns on mouse press against edge WindowsCanCover, ///< Windows are allowed to go above the panel, it raises on mouse press against screen edge WindowsGoBelow ///< Window are allowed to go below the panel }; /** * @returns The PanelBehavior for a PlasmaShellSurfaceInterface with role @c Role::Panel * @see role **/ PanelBehavior panelBehavior() const; /** * @returns true if this window doesn't want to be listed * in the taskbar * @since 5.5 */ bool skipTaskbar() const; + /** + * Informs the PlasmaShellSurfaceInterface that the auto-hiding panel got hidden. + * Once it is shown again the method @link{showAutoHidingPanel} should be used. + * + * @see showAutoHidingPanel + * @see panelAutoHideHideRequested + * @see panelAutoHideShowRequested + * @since 5.28 + **/ + void hideAutoHidingPanel(); + + /** + * Informs the PlasmaShellSurfaceInterface that the auto-hiding panel got shown again. + * + * @see hideAutoHidingPanel + * @see panelAutoHideHideRequested + * @see panelAutoHideShowRequested + * @see 5.28 + **/ + void showAutoHidingPanel(); + /** * @returns The PlasmaShellSurfaceInterface for the @p native resource. * @since 5.5 **/ static PlasmaShellSurfaceInterface *get(wl_resource *native); Q_SIGNALS: /** * A change of global position has been requested. **/ void positionChanged(); /** * A change of the role has been requested. **/ void roleChanged(); /** * A change of the panel behavior has been requested. **/ void panelBehaviorChanged(); /** * A change in the skip taskbar property has been requested */ void skipTaskbarChanged(); + /** + * A surface with Role Panel and PanelBehavior AutoHide requested to be hidden. + * + * The compositor should inform the PlasmaShellSurfaceInterface about the actual change. + * Once the surface is hidden it should invoke @link{hideAutoHidingPanel}. If the compositor + * cannot hide the surface (e.g. because it doesn't border a screen edge) it should inform + * the surface through invoking @link{showAutoHidingPanel}. This method should also be invoked + * whenever the surface gets shown again due to triggering the screen edge. + * + * @see hideAutoHidingPanel + * @see showAutoHidingPanel + * @see panelAutoHideShowRequested + * @since 5.28 + **/ + void panelAutoHideHideRequested(); + + /** + * A surface with Role Panel and PanelBehavior AutoHide requested to be shown. + * + * The compositor should inform the PlasmaShellSurfaceInterface about the actual change. + * Once the surface is shown it should invoke @link{showAutoHidingPanel}. + * + * @see hideAutoHidingPanel + * @see showAutoHidingPanel + * @see panelAutoHideHideRequested + * @since 5.28 + **/ + void panelAutoHideShowRequested(); + private: friend class PlasmaShellInterface; explicit PlasmaShellSurfaceInterface(PlasmaShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::PlasmaShellSurfaceInterface::Role) Q_DECLARE_METATYPE(KWayland::Server::PlasmaShellSurfaceInterface::PanelBehavior) #endif