diff --git a/autotests/client/test_wayland_windowmanagement.cpp b/autotests/client/test_wayland_windowmanagement.cpp index 9a6a720..f940486 100644 --- a/autotests/client/test_wayland_windowmanagement.cpp +++ b/autotests/client/test_wayland_windowmanagement.cpp @@ -1,313 +1,344 @@ /******************************************************************** Copyright 2015 Marco Martin 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 // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/plasmawindowmanagement.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/plasmawindowmanagement_interface.h" #include "../../src/server/surface_interface.h" class TestWindowManagement : public QObject { Q_OBJECT public: explicit TestWindowManagement(QObject *parent = nullptr); private Q_SLOTS: void init(); void testWindowTitle(); void testMinimizedGeometry(); void testUseAfterUnmap(); void testServerDelete(); void testActiveWindowOnUnmapped(); void testDeleteActiveWindow(); + void testCreateAfterUnmap(); void cleanup(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagementInterface; KWayland::Server::PlasmaWindowInterface *m_windowInterface; KWayland::Server::SurfaceInterface *m_surfaceInterface = nullptr; KWayland::Client::Surface *m_surface = nullptr; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::EventQueue *m_queue; KWayland::Client::PlasmaWindowManagement *m_windowManagement; KWayland::Client::PlasmaWindow *m_window; QThread *m_thread; KWayland::Client::Registry *m_registry; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-windowmanagement-0"); TestWindowManagement::TestWindowManagement(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWindowManagement::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); 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 KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new KWayland::Client::Registry(this); QSignalSpy compositorSpy(m_registry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorSpy.isValid()); QSignalSpy windowManagementSpy(m_registry, SIGNAL(plasmaWindowManagementAnnounced(quint32,quint32))); QVERIFY(windowManagementSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection->display()); QVERIFY(m_registry->isValid()); m_registry->setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = m_registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_windowManagementInterface = m_display->createPlasmaWindowManagement(m_display); m_windowManagementInterface->create(); QVERIFY(m_windowManagementInterface->isValid()); QVERIFY(windowManagementSpy.wait()); m_windowManagement = m_registry->createPlasmaWindowManagement(windowManagementSpy.first().first().value(), windowManagementSpy.first().last().value(), this); QSignalSpy windowSpy(m_windowManagement, SIGNAL(windowCreated(KWayland::Client::PlasmaWindow *))); QVERIFY(windowSpy.isValid()); m_windowInterface = m_windowManagementInterface->createWindow(this); QVERIFY(windowSpy.wait()); m_window = windowSpy.first().first().value(); QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); m_surface = m_compositor->createSurface(this); QVERIFY(m_surface); QVERIFY(serverSurfaceCreated.wait()); m_surfaceInterface = serverSurfaceCreated.first().first().value(); QVERIFY(m_surfaceInterface); } void TestWindowManagement::testWindowTitle() { m_windowInterface->setTitle(QStringLiteral("Test Title")); QSignalSpy titleSpy(m_window, SIGNAL(titleChanged())); QVERIFY(titleSpy.isValid()); QVERIFY(titleSpy.wait()); QCOMPARE(m_window->title(), QString::fromUtf8("Test Title")); } void TestWindowManagement::testMinimizedGeometry() { m_window->setMinimizedGeometry(m_surface, QRect(5, 10, 100, 200)); QSignalSpy geometrySpy(m_windowInterface, SIGNAL(minimizedGeometriesChanged())); QVERIFY(geometrySpy.isValid()); QVERIFY(geometrySpy.wait()); QCOMPARE(m_windowInterface->minimizedGeometries().values().first(), QRect(5, 10, 100, 200)); m_window->unsetMinimizedGeometry(m_surface); QVERIFY(geometrySpy.wait()); QVERIFY(m_windowInterface->minimizedGeometries().isEmpty()); } void TestWindowManagement::cleanup() { if (m_surface) { delete m_surface; m_surface = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_windowManagement) { delete m_windowManagement; m_windowManagement = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_thread) { if (m_connection) { m_connection->deleteLater(); } m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } m_connection = nullptr; delete m_windowManagementInterface; m_windowManagementInterface = nullptr; delete m_windowInterface; m_windowInterface = nullptr; delete m_surfaceInterface; m_surfaceInterface = nullptr; delete m_display; m_display = nullptr; } void TestWindowManagement::testUseAfterUnmap() { // this test verifies that when the client uses a window after it's unmapped, things don't break QSignalSpy unmappedSpy(m_window, &KWayland::Client::PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); m_windowInterface->unmap(); m_window->requestClose(); QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.wait()); m_window = nullptr; QSignalSpy serverDestroyedSpy(m_windowInterface, &QObject::destroyed); QVERIFY(serverDestroyedSpy.isValid()); QVERIFY(serverDestroyedSpy.wait()); m_windowInterface = nullptr; } void TestWindowManagement::testServerDelete() { QSignalSpy unmappedSpy(m_window, &KWayland::Client::PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete m_windowInterface; m_windowInterface = nullptr; QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.wait()); m_window = nullptr; } void TestWindowManagement::testActiveWindowOnUnmapped() { // This test verifies that unmapping the active window changes the active window. // first make the window active QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(!m_window->isActive()); QSignalSpy activeWindowChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::activeWindowChanged); QVERIFY(activeWindowChangedSpy.isValid()); m_windowInterface->setActive(true); QVERIFY(activeWindowChangedSpy.wait()); QCOMPARE(m_windowManagement->activeWindow(), m_window); QVERIFY(m_window->isActive()); // now unmap should change the active window QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); QSignalSpy serverDestroyedSpy(m_windowInterface, &QObject::destroyed); QVERIFY(serverDestroyedSpy.isValid()); m_windowManagementInterface->unmapWindow(m_windowInterface); QVERIFY(activeWindowChangedSpy.wait()); QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(destroyedSpy.wait()); QCOMPARE(destroyedSpy.count(), 1); m_window = nullptr; QVERIFY(serverDestroyedSpy.wait()); m_windowInterface = nullptr; } void TestWindowManagement::testDeleteActiveWindow() { // This test verifies that deleting the active window on client side changes the active window // first make the window active QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(!m_window->isActive()); QSignalSpy activeWindowChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::activeWindowChanged); QVERIFY(activeWindowChangedSpy.isValid()); m_windowInterface->setActive(true); QVERIFY(activeWindowChangedSpy.wait()); QCOMPARE(activeWindowChangedSpy.count(), 1); QCOMPARE(m_windowManagement->activeWindow(), m_window); QVERIFY(m_window->isActive()); // delete the client side window - that's semantically kind of wrong, but shouldn't make our code crash delete m_window; m_window = nullptr; QCOMPARE(activeWindowChangedSpy.count(), 2); QVERIFY(!m_windowManagement->activeWindow()); } +void TestWindowManagement::testCreateAfterUnmap() +{ + // this test verifies that we don't get a protocol error on client side when creating an already unmapped window. + QSignalSpy windowSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated); + QVERIFY(windowSpy.isValid()); + // create and unmap in one go + // client will first handle the create, the unmap will be sent once the server side is bound + auto serverWindow = m_windowManagementInterface->createWindow(this); + serverWindow->unmap(); + QCOMPARE(m_windowManagementInterface->children().count(), 0); + QVERIFY(windowSpy.wait()); + QCOMPARE(windowSpy.count(), 1); + auto window = windowSpy.first().first().value(); + QVERIFY(window); + // now this is not yet on the server, on the server it will be after next roundtrip + // which we can trigger by waiting for destroy of the newly created window. + // why destroy? Because we will get the unmap which triggers a destroy + QSignalSpy clientDestroyedSpy(window, &QObject::destroyed); + QVERIFY(clientDestroyedSpy.isValid()); + QVERIFY(clientDestroyedSpy.wait()); + // the server side created a helper PlasmaWindowInterface with PlasmaWindowManagementInterface as parent + // it emitted unmapped so we can be sure it will be destroyed directly + QCOMPARE(m_windowManagementInterface->children().count(), 1); + auto helperWindow = qobject_cast(m_windowManagementInterface->children().first()); + QVERIFY(helperWindow); + QSignalSpy helperDestroyedSpy(helperWindow, &QObject::destroyed); + QVERIFY(helperDestroyedSpy.isValid()); + QVERIFY(helperDestroyedSpy.wait()); +} + QTEST_GUILESS_MAIN(TestWindowManagement) #include "test_wayland_windowmanagement.moc" diff --git a/src/server/plasmawindowmanagement_interface.cpp b/src/server/plasmawindowmanagement_interface.cpp index bf133e8..d106941 100644 --- a/src/server/plasmawindowmanagement_interface.cpp +++ b/src/server/plasmawindowmanagement_interface.cpp @@ -1,657 +1,654 @@ /******************************************************************** 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 "plasmawindowmanagement_interface.h" #include "global_p.h" #include "resource_p.h" #include "display.h" #include "surface_interface.h" #include #include #include #include #include #include namespace KWayland { namespace Server { class PlasmaWindowManagementInterface::Private : public Global::Private { public: Private(PlasmaWindowManagementInterface *q, Display *d); void sendShowingDesktopState(); ShowingDesktopState state = ShowingDesktopState::Disabled; QVector resources; QList windows; quint32 windowIdCounter = 0; private: static void unbind(wl_resource *resource); static void showDesktopCallback(wl_client *client, wl_resource *resource, uint32_t state); static void getWindowCallback(wl_client *client, wl_resource *resource, uint32_t id, uint32_t internalWindowId); void bind(wl_client *client, uint32_t version, uint32_t id) override; void sendShowingDesktopState(wl_resource *r); PlasmaWindowManagementInterface *q; static const struct org_kde_plasma_window_management_interface s_interface; static const quint32 s_version; }; class PlasmaWindowInterface::Private { public: Private(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q); ~Private(); void createResource(wl_resource *parent, uint32_t id); void setTitle(const QString &title); void setAppId(const QString &appId); void setThemedIconName(const QString &iconName); void setVirtualDesktop(quint32 desktop); void unmap(); void setState(org_kde_plasma_window_management_state flag, bool set); QVector resources; quint32 windowId = 0; QHash minimizedGeometries; PlasmaWindowManagementInterface *wm; bool unmapped = false; private: static void unbind(wl_resource *resource); static void setStateCallback(wl_client *client, wl_resource *resource, uint32_t flags, uint32_t state); static void setVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t number); static void closeCallback(wl_client *client, wl_resource *resource); static void requestMoveCallback(wl_client *client, wl_resource *resource); static void requestResizeCallback(wl_client *client, wl_resource *resource); static void setMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height); static void unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel); static void destroyCallback(wl_client *client, wl_resource *resource); static Private *cast(wl_resource *resource) { return reinterpret_cast(wl_resource_get_user_data(resource)); } PlasmaWindowInterface *q; QString m_title; QString m_appId; QString m_themedIconName; quint32 m_virtualDesktop = 0; quint32 m_state = 0; wl_listener listener; static const struct org_kde_plasma_window_interface s_interface; }; const quint32 PlasmaWindowManagementInterface::Private::s_version = 4; PlasmaWindowManagementInterface::Private::Private(PlasmaWindowManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_window_management_interface, s_version) , q(q) { } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_plasma_window_management_interface PlasmaWindowManagementInterface::Private::s_interface = { showDesktopCallback, getWindowCallback }; #endif void PlasmaWindowManagementInterface::Private::sendShowingDesktopState() { for (wl_resource *r : resources) { sendShowingDesktopState(r); } } void PlasmaWindowManagementInterface::Private::sendShowingDesktopState(wl_resource *r) { uint32_t s = 0; switch (state) { case ShowingDesktopState::Enabled: s = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED; break; case ShowingDesktopState::Disabled: s = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED; break; default: Q_UNREACHABLE(); break; } org_kde_plasma_window_management_send_show_desktop_changed(r, s); } void PlasmaWindowManagementInterface::Private::showDesktopCallback(wl_client *client, wl_resource *resource, uint32_t state) { Q_UNUSED(client) ShowingDesktopState s = ShowingDesktopState::Disabled; switch (state) { case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED: s = ShowingDesktopState::Enabled; break; case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED: default: s = ShowingDesktopState::Disabled; break; } emit reinterpret_cast(wl_resource_get_user_data(resource))->q->requestChangeShowingDesktop(s); } void PlasmaWindowManagementInterface::Private::getWindowCallback(wl_client *client, wl_resource *resource, uint32_t id, uint32_t internalWindowId) { auto p = reinterpret_cast(wl_resource_get_user_data(resource)); auto it = std::find_if(p->windows.constBegin(), p->windows.constEnd(), [internalWindowId] (PlasmaWindowInterface *window) { return window->d->windowId == internalWindowId; } ); if (it == p->windows.constEnd()) { - ClientConnection *c = p->q->display()->getConnection(client); - wl_resource *r = c->createResource(&org_kde_plasma_window_interface, wl_resource_get_version(resource), id); - if (!r) { - return; - } - org_kde_plasma_window_send_unmapped(r); - wl_resource_destroy(r); + // create a temp window just for the resource and directly send an unmapped + PlasmaWindowInterface *window = new PlasmaWindowInterface(p->q, p->q); + window->d->createResource(resource, id); + window->unmap(); return; } (*it)->d->createResource(resource, id); } PlasmaWindowManagementInterface::PlasmaWindowManagementInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } PlasmaWindowManagementInterface::~PlasmaWindowManagementInterface() = default; void PlasmaWindowManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *shell = c->createResource(&org_kde_plasma_window_management_interface, qMin(version, s_version), id); if (!shell) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(shell, &s_interface, this, unbind); resources << shell; for (auto it = windows.constBegin(); it != windows.constEnd(); ++it) { org_kde_plasma_window_management_send_window(shell, (*it)->d->windowId); } } void PlasmaWindowManagementInterface::Private::unbind(wl_resource *resource) { auto wm = reinterpret_cast(wl_resource_get_user_data(resource)); wm->resources.removeAll(resource); } void PlasmaWindowManagementInterface::setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState state) { Q_D(); if (d->state == state) { return; } d->state = state; d->sendShowingDesktopState(); } PlasmaWindowManagementInterface::Private *PlasmaWindowManagementInterface::d_func() const { return reinterpret_cast(d.data()); } PlasmaWindowInterface *PlasmaWindowManagementInterface::createWindow(QObject *parent) { Q_D(); PlasmaWindowInterface *window = new PlasmaWindowInterface(this, parent); // TODO: improve window ids so that it cannot wrap around window->d->windowId = ++d->windowIdCounter; for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_management_send_window(*it, window->d->windowId); } d->windows << window; connect(window, &QObject::destroyed, this, [this, window] { Q_D(); d->windows.removeAll(window); } ); return window; } void PlasmaWindowManagementInterface::unmapWindow(PlasmaWindowInterface *window) { if (!window) { return; } Q_D(); d->windows.removeOne(window); Q_ASSERT(!d->windows.contains(window)); window->d->unmap(); } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_plasma_window_interface PlasmaWindowInterface::Private::s_interface = { setStateCallback, setVirtualDesktopCallback, setMinimizedGeometryCallback, unsetMinimizedGeometryCallback, closeCallback, requestMoveCallback, requestResizeCallback, destroyCallback }; #endif PlasmaWindowInterface::Private::Private(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q) : wm(wm) , q(q) { } PlasmaWindowInterface::Private::~Private() { // need to copy, as destroy goes through the destroy listener and modifies the list as we iterate const auto c = resources; for (const auto &r : c) { auto client = wl_resource_get_client(r); org_kde_plasma_window_send_unmapped(r); wl_resource_destroy(r); wl_client_flush(client); } } void PlasmaWindowInterface::Private::destroyCallback(wl_client *, wl_resource *r) { Private *p = cast(r); p->resources.removeAll(r); wl_resource_destroy(r); if (p->unmapped && p->resources.isEmpty()) { p->q->deleteLater(); } } void PlasmaWindowInterface::Private::unbind(wl_resource *resource) { Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); p->resources.removeAll(resource); if (p->unmapped && p->resources.isEmpty()) { p->q->deleteLater(); } } void PlasmaWindowInterface::Private::createResource(wl_resource *parent, uint32_t id) { ClientConnection *c = wm->display()->getConnection(wl_resource_get_client(parent)); wl_resource *resource = c->createResource(&org_kde_plasma_window_interface, wl_resource_get_version(parent), id); if (!resource) { return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); resources << resource; org_kde_plasma_window_send_virtual_desktop_changed(resource, m_virtualDesktop); if (!m_appId.isEmpty()) { org_kde_plasma_window_send_app_id_changed(resource, m_appId.toUtf8().constData()); } if (!m_title.isEmpty()) { org_kde_plasma_window_send_title_changed(resource, m_title.toUtf8().constData()); } org_kde_plasma_window_send_state_changed(resource, m_state); org_kde_plasma_window_send_themed_icon_name_changed(resource, m_themedIconName.toUtf8().constData()); c->flush(); } void PlasmaWindowInterface::Private::setAppId(const QString &appId) { if (m_appId == appId) { return; } m_appId = appId; const QByteArray utf8 = m_appId.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_app_id_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setThemedIconName(const QString &iconName) { if (m_themedIconName == iconName) { return; } m_themedIconName = iconName; const QByteArray utf8 = m_themedIconName.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_themed_icon_name_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setTitle(const QString &title) { if (m_title == title) { return; } m_title = title; const QByteArray utf8 = m_title.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_title_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setVirtualDesktop(quint32 desktop) { if (m_virtualDesktop == desktop) { return; } m_virtualDesktop = desktop; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_changed(*it, m_virtualDesktop); } } void PlasmaWindowInterface::Private::unmap() { unmapped = true; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_unmapped(*it); } if (resources.isEmpty()) { q->deleteLater(); } } void PlasmaWindowInterface::Private::setState(org_kde_plasma_window_management_state flag, bool set) { quint32 newState = m_state; if (set) { newState |= flag; } else { newState &= ~flag; } if (newState == m_state) { return; } m_state = newState; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_state_changed(*it, m_state); } } void PlasmaWindowInterface::Private::closeCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->closeRequested(); } void PlasmaWindowInterface::Private::requestMoveCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->moveRequested(); } void PlasmaWindowInterface::Private::requestResizeCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->resizeRequested(); } void PlasmaWindowInterface::Private::setVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t number) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->virtualDesktopRequested(number); } void PlasmaWindowInterface::Private::setStateCallback(wl_client *client, wl_resource *resource, uint32_t flags, uint32_t state) { Q_UNUSED(client) Private *p = cast(resource); if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) { emit p->q->activeRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED) { emit p->q->minimizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED) { emit p->q->maximizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN) { emit p->q->fullscreenRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE) { emit p->q->keepAboveRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW) { emit p->q->keepBelowRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION) { emit p->q->demandsAttentionRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE) { emit p->q->closeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE) { emit p->q->minimizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE) { emit p->q->maximizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE) { emit p->q->fullscreenableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR) { emit p->q->skipTaskbarRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE) { emit p->q->shadeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED) { emit p->q->shadedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE) { emit p->q->movableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE) { emit p->q->resizableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE) { emit p->q->virtualDesktopChangeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } } void PlasmaWindowInterface::Private::setMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { Q_UNUSED(client) Private *p = cast(resource); SurfaceInterface *panelSurface = SurfaceInterface::get(panel); if (!panelSurface) { return; } if (p->minimizedGeometries.value(panelSurface) == QRect(x, y, width, height)) { return; } p->minimizedGeometries[panelSurface] = QRect(x, y, width, height); emit p->q->minimizedGeometriesChanged(); connect(panelSurface, &QObject::destroyed, p->q, [p, panelSurface] () { if (p->minimizedGeometries.remove(panelSurface)) { emit p->q->minimizedGeometriesChanged(); } }); } void PlasmaWindowInterface::Private::unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel) { Q_UNUSED(client) Private *p = cast(resource); SurfaceInterface *panelSurface = SurfaceInterface::get(panel); if (!panelSurface) { return; } if (!p->minimizedGeometries.contains(panelSurface)) { return; } p->minimizedGeometries.remove(panelSurface); emit p->q->minimizedGeometriesChanged(); } PlasmaWindowInterface::PlasmaWindowInterface(PlasmaWindowManagementInterface *wm, QObject *parent) : QObject(parent) , d(new Private(wm, this)) { } PlasmaWindowInterface::~PlasmaWindowInterface() = default; void PlasmaWindowInterface::setAppId(const QString &appId) { d->setAppId(appId); } void PlasmaWindowInterface::setTitle(const QString &title) { d->setTitle(title); } void PlasmaWindowInterface::setVirtualDesktop(quint32 desktop) { d->setVirtualDesktop(desktop); } void PlasmaWindowInterface::unmap() { d->wm->unmapWindow(this); } QHash PlasmaWindowInterface::minimizedGeometries() const { return d->minimizedGeometries; } void PlasmaWindowInterface::setActive(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, set); } void PlasmaWindowInterface::setFullscreen(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, set); } void PlasmaWindowInterface::setKeepAbove(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, set); } void PlasmaWindowInterface::setKeepBelow(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, set); } void PlasmaWindowInterface::setMaximized(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, set); } void PlasmaWindowInterface::setMinimized(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, set); } void PlasmaWindowInterface::setOnAllDesktops(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, set); } void PlasmaWindowInterface::setDemandsAttention(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION, set); } void PlasmaWindowInterface::setCloseable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE, set); } void PlasmaWindowInterface::setFullscreenable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE, set); } void PlasmaWindowInterface::setMaximizeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE, set); } void PlasmaWindowInterface::setMinimizeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE, set); } void PlasmaWindowInterface::setSkipTaskbar(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR, set); } void PlasmaWindowInterface::setThemedIconName(const QString &iconName) { d->setThemedIconName(iconName); } void PlasmaWindowInterface::setShadeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set); } void PlasmaWindowInterface::setShaded(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, set); } void PlasmaWindowInterface::setMovable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE, set); } void PlasmaWindowInterface::setResizable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE, set); } void PlasmaWindowInterface::setVirtualDesktopChangeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE, set); } } }