diff --git a/abstract_client.h b/abstract_client.h --- a/abstract_client.h +++ b/abstract_client.h @@ -873,6 +873,11 @@ **/ virtual bool supportsWindowRules() const; + /** + * Returns extents of the decoration for each side. + **/ + QMargins frameMargins() const; + public Q_SLOTS: virtual void closeWindow() = 0; diff --git a/abstract_client.cpp b/abstract_client.cpp --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -2092,4 +2092,9 @@ return true; } +QMargins AbstractClient::frameMargins() const +{ + return QMargins(borderLeft(), borderTop(), borderRight(), borderBottom()); +} + } diff --git a/autotests/integration/debug_console_test.cpp b/autotests/integration/debug_console_test.cpp --- a/autotests/integration/debug_console_test.cpp +++ b/autotests/integration/debug_console_test.cpp @@ -18,11 +18,13 @@ along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" -#include "platform.h" #include "debug_console.h" +#include "internal_client.h" +#include "platform.h" #include "screens.h" #include "shell_client.h" #include "wayland_server.h" +#include "workspace.h" #include "xcbutils.h" #include @@ -57,8 +59,9 @@ void DebugConsoleTest::initTestCase() { - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); @@ -511,12 +514,12 @@ QSignalSpy destroyedSpy(console, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); console->show(); QCOMPARE(console->windowHandle()->isVisible(), true); QTRY_COMPARE(clientAddedSpy.count(), 1); - ShellClient *c = clientAddedSpy.first().first().value(); + InternalClient *c = clientAddedSpy.first().first().value(); QVERIFY(c->isInternal()); QCOMPARE(c->internalWindow(), console->windowHandle()); QVERIFY(c->isDecorated()); diff --git a/autotests/integration/decoration_input_test.cpp b/autotests/integration/decoration_input_test.cpp --- a/autotests/integration/decoration_input_test.cpp +++ b/autotests/integration/decoration_input_test.cpp @@ -18,16 +18,17 @@ along with this program. If not, see . *********************************************************************/ #include "kwin_wayland_test.h" -#include "platform.h" #include "abstract_client.h" #include "cursor.h" +#include "internal_client.h" +#include "platform.h" #include "pointer_input.h" #include "touch_input.h" #include "screenedge.h" #include "screens.h" +#include "shell_client.h" #include "wayland_server.h" #include "workspace.h" -#include "shell_client.h" #include #include "decorations/decoratedclient.h" @@ -133,8 +134,8 @@ void DecorationInputTest::initTestCase() { - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); @@ -910,12 +911,12 @@ QSignalSpy keyEvent(keyboard, &KWayland::Client::Keyboard::keyChanged); QVERIFY(keyEvent.isValid()); - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); c->decoratedClient()->requestShowToolTip(QStringLiteral("test")); // now we should get an internal window QVERIFY(clientAddedSpy.wait()); - ShellClient *internal = clientAddedSpy.first().first().value(); + InternalClient *internal = clientAddedSpy.first().first().value(); QVERIFY(internal->isInternal()); QVERIFY(internal->internalWindow()->flags().testFlag(Qt::ToolTip)); diff --git a/autotests/integration/effects/popup_open_close_animation_test.cpp b/autotests/integration/effects/popup_open_close_animation_test.cpp --- a/autotests/integration/effects/popup_open_close_animation_test.cpp +++ b/autotests/integration/effects/popup_open_close_animation_test.cpp @@ -24,6 +24,7 @@ #include "deleted.h" #include "effectloader.h" #include "effects.h" +#include "internal_client.h" #include "platform.h" #include "shell_client.h" #include "useractions.h" @@ -248,11 +249,11 @@ QVERIFY(!effect->isActive()); // Show a decoration tooltip. - QSignalSpy tooltipAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy tooltipAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(tooltipAddedSpy.isValid()); client->decoratedClient()->requestShowToolTip(QStringLiteral("KWin rocks!")); QVERIFY(tooltipAddedSpy.wait()); - ShellClient *tooltip = tooltipAddedSpy.first().first().value(); + InternalClient *tooltip = tooltipAddedSpy.first().first().value(); QVERIFY(tooltip->isInternal()); QVERIFY(tooltip->isPopupWindow()); QVERIFY(tooltip->internalWindow()->flags().testFlag(Qt::ToolTip)); @@ -262,7 +263,7 @@ QTRY_VERIFY(!effect->isActive()); // Hide the decoration tooltip. - QSignalSpy tooltipClosedSpy(tooltip, &ShellClient::windowClosed); + QSignalSpy tooltipClosedSpy(tooltip, &InternalClient::windowClosed); QVERIFY(tooltipClosedSpy.isValid()); client->decoratedClient()->requestHideToolTip(); QVERIFY(tooltipClosedSpy.wait()); diff --git a/autotests/integration/internal_window.cpp b/autotests/integration/internal_window.cpp --- a/autotests/integration/internal_window.cpp +++ b/autotests/integration/internal_window.cpp @@ -22,8 +22,8 @@ #include "cursor.h" #include "effects.h" #include "internal_client.h" -#include "shell_client.h" #include "screens.h" +#include "shell_client.h" #include "wayland_server.h" #include "workspace.h" @@ -184,8 +184,8 @@ void InternalWindowTest::initTestCase() { - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); @@ -215,22 +215,22 @@ void InternalWindowTest::testEnterLeave() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; - QVERIFY(!workspace()->findToplevel(nullptr)); - QVERIFY(!workspace()->findToplevel(&win)); + QVERIFY(!workspace()->findInternal(nullptr)); + QVERIFY(!workspace()->findInternal(&win)); win.setGeometry(0, 0, 100, 100); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); QVERIFY(!workspace()->activeClient()); - ShellClient *c = clientAddedSpy.first().first().value(); + InternalClient *c = clientAddedSpy.first().first().value(); QVERIFY(c->isInternal()); QVERIFY(qobject_cast(c)); QCOMPARE(c->icon().name(), QStringLiteral("wayland")); QVERIFY(!c->isDecorated()); - QCOMPARE(workspace()->findToplevel(&win), c); + QCOMPARE(workspace()->findInternal(&win), c); QCOMPARE(c->geometry(), QRect(0, 0, 100, 100)); QVERIFY(c->isShown(false)); QVERIFY(workspace()->xStackingOrder().contains(c)); @@ -276,7 +276,7 @@ void InternalWindowTest::testPointerPressRelease() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); @@ -299,7 +299,7 @@ void InternalWindowTest::testPointerAxis() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); @@ -327,7 +327,7 @@ void InternalWindowTest::testKeyboard() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); @@ -337,7 +337,7 @@ QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased); QVERIFY(releaseSpy.isValid()); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QVERIFY(internalClient->isInternal()); QVERIFY(internalClient->readyForPainting()); @@ -356,7 +356,7 @@ void InternalWindowTest::testKeyboardShowWithoutActivating() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setProperty("_q_showWithoutActivating", true); @@ -367,7 +367,7 @@ QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased); QVERIFY(releaseSpy.isValid()); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QVERIFY(internalClient->isInternal()); QVERIFY(internalClient->readyForPainting()); @@ -412,7 +412,7 @@ QCOMPARE(enteredSpy.count(), 1); // create internal window - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); @@ -422,7 +422,7 @@ QSignalSpy releaseSpy(&win, &HelperWindow::keyReleased); QVERIFY(releaseSpy.isValid()); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QVERIFY(internalClient->isInternal()); QVERIFY(internalClient->readyForPainting()); @@ -449,7 +449,7 @@ void InternalWindowTest::testTouch() { // touch events for internal windows are emulated through mouse events - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); @@ -513,36 +513,36 @@ void InternalWindowTest::testOpacity() { - // this test verifies that opacity is properly synced from QWindow to ShellClient - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + // this test verifies that opacity is properly synced from QWindow to InternalClient + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setOpacity(0.5); win.setGeometry(0, 0, 100, 100); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QVERIFY(internalClient->isInternal()); QCOMPARE(internalClient->opacity(), 0.5); - QSignalSpy opacityChangedSpy(internalClient, &ShellClient::opacityChanged); + QSignalSpy opacityChangedSpy(internalClient, &InternalClient::opacityChanged); QVERIFY(opacityChangedSpy.isValid()); win.setOpacity(0.75); QCOMPARE(opacityChangedSpy.count(), 1); QCOMPARE(internalClient->opacity(), 0.75); } void InternalWindowTest::testMove() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setOpacity(0.5); win.setGeometry(0, 0, 100, 100); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QCOMPARE(internalClient->geometry(), QRect(0, 0, 100, 100)); @@ -576,16 +576,16 @@ void InternalWindowTest::testSkipCloseAnimation() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setOpacity(0.5); win.setGeometry(0, 0, 100, 100); QFETCH(bool, initial); win.setProperty("KWIN_SKIP_CLOSE_ANIMATION", initial); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QCOMPARE(internalClient->skipsCloseAnimation(), initial); QSignalSpy skipCloseChangedSpy(internalClient, &Toplevel::skipCloseAnimationChanged); @@ -600,14 +600,14 @@ void InternalWindowTest::testModifierClickUnrestrictedMove() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); win.setFlags(win.flags() & ~Qt::FramelessWindowHint); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QVERIFY(internalClient->isDecorated()); @@ -642,14 +642,14 @@ void InternalWindowTest::testModifierScroll() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); win.setFlags(win.flags() & ~Qt::FramelessWindowHint); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QVERIFY(internalClient->isDecorated()); @@ -676,14 +676,14 @@ void InternalWindowTest::testPopup() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); win.setFlags(win.flags() | Qt::Popup); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QCOMPARE(internalClient->isPopupWindow(), true); } @@ -695,15 +695,15 @@ Q_ARG(QVector, QVector({QRect(0,0,1280, 1024), QRect(1280/2, 0, 1280, 1024)})), Q_ARG(QVector, QVector({2,2}))); - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); win.setFlags(win.flags() | Qt::Popup); win.show(); QCOMPARE(win.devicePixelRatio(), 2.0); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QCOMPARE(internalClient->surface()->scale(), 2); QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); @@ -732,15 +732,15 @@ void InternalWindowTest::testWindowType() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); QFETCH(NET::WindowType, windowType); KWindowSystem::setType(win.winId(), windowType); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QCOMPARE(internalClient->windowType(), windowType); } @@ -767,13 +767,13 @@ void InternalWindowTest::testChangeWindowType() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QCOMPARE(internalClient->windowType(), NET::Normal); @@ -787,13 +787,13 @@ void InternalWindowTest::testEffectWindow() { - QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy clientAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(clientAddedSpy.isValid()); HelperWindow win; win.setGeometry(0, 0, 100, 100); win.show(); QTRY_COMPARE(clientAddedSpy.count(), 1); - auto internalClient = clientAddedSpy.first().first().value(); + auto internalClient = clientAddedSpy.first().first().value(); QVERIFY(internalClient); QVERIFY(internalClient->effectWindow()); QCOMPARE(internalClient->effectWindow()->internalWindow(), &win); diff --git a/composite.cpp b/composite.cpp --- a/composite.cpp +++ b/composite.cpp @@ -24,6 +24,7 @@ #include "decorations/decoratedclient.h" #include "deleted.h" #include "effects.h" +#include "internal_client.h" #include "overlaywindow.h" #include "platform.h" #include "scene.h" @@ -342,17 +343,17 @@ c->setupCompositing(); c->getShadow(); } + const InternalClientList internalClients = workspace()->internalClients(); + for (InternalClient *c : internalClients) { + c->setupCompositing(); + c->getShadow(); + } if (auto w = waylandServer()) { const auto clients = w->clients(); for (auto c : clients) { c->setupCompositing(); c->getShadow(); } - const auto internalClients = w->internalClients(); - for (auto c : internalClients) { - c->setupCompositing(); - c->getShadow(); - } } m_state = State::On; @@ -389,18 +390,25 @@ effects = nullptr; if (Workspace::self()) { + const InternalClientList internalClients = workspace()->internalClients(); foreach (Client * c, Workspace::self()->clientList()) m_scene->removeToplevel(c); foreach (Client * c, Workspace::self()->desktopList()) m_scene->removeToplevel(c); foreach (Unmanaged * c, Workspace::self()->unmanagedList()) m_scene->removeToplevel(c); + for (InternalClient *client : internalClients) { + m_scene->removeToplevel(client); + } foreach (Client * c, Workspace::self()->clientList()) c->finishCompositing(); foreach (Client * c, Workspace::self()->desktopList()) c->finishCompositing(); foreach (Unmanaged * c, Workspace::self()->unmanagedList()) c->finishCompositing(); + for (InternalClient *client : internalClients) { + client->finishCompositing(); + } if (auto c = kwinApp()->x11Connection()) { xcb_composite_unredirect_subwindows(c, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); } @@ -412,15 +420,9 @@ foreach (ShellClient *c, waylandServer()->clients()) { m_scene->removeToplevel(c); } - foreach (ShellClient *c, waylandServer()->internalClients()) { - m_scene->removeToplevel(c); - } foreach (ShellClient *c, waylandServer()->clients()) { c->finishCompositing(); } - foreach (ShellClient *c, waylandServer()->internalClients()) { - c->finishCompositing(); - } } delete m_scene; m_scene = NULL; @@ -793,13 +795,13 @@ if (std::any_of(clients.begin(), clients.end(), test)) { return true; } - const auto &internalClients = w->internalClients(); - auto internalTest = [] (ShellClient *c) { - return c->isShown(true) && !c->repaints().isEmpty(); - }; - if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { - return true; - } + } + const auto &internalClients = workspace()->internalClients(); + auto internalTest = [] (InternalClient *c) { + return c->isShown(true) && !c->repaints().isEmpty(); + }; + if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) { + return true; } return false; } diff --git a/debug_console.h b/debug_console.h --- a/debug_console.h +++ b/debug_console.h @@ -40,6 +40,7 @@ { class Client; +class InternalClient; class ShellClient; class Unmanaged; class DebugConsoleFilter; @@ -73,13 +74,13 @@ template void remove(int parentRow, QVector &clients, T *client); ShellClient *shellClient(const QModelIndex &index) const; - ShellClient *internalClient(const QModelIndex &index) const; + InternalClient *internalClient(const QModelIndex &index) const; Client *x11Client(const QModelIndex &index) const; Unmanaged *unmanaged(const QModelIndex &index) const; int topLevelRowCount() const; QVector m_shellClients; - QVector m_internalClients; + QVector m_internalClients; QVector m_x11Clients; QVector m_unmanageds; diff --git a/debug_console.cpp b/debug_console.cpp --- a/debug_console.cpp +++ b/debug_console.cpp @@ -21,6 +21,7 @@ #include "composite.h" #include "client.h" #include "input_event.h" +#include "internal_client.h" #include "main.h" #include "scene.h" #include "shell_client.h" @@ -795,23 +796,14 @@ for (auto c : clients) { m_shellClients.append(c); } - const auto internals = waylandServer()->internalClients(); - for (auto c : internals) { - m_internalClients.append(c); - } // TODO: that only includes windows getting shown, not those which are only created connect(waylandServer(), &WaylandServer::shellClientAdded, this, [this] (ShellClient *c) { - if (c->isInternal()) { - add(s_waylandInternalId -1, m_internalClients, c); - } else { - add(s_waylandClientId -1, m_shellClients, c); - } + add(s_waylandClientId -1, m_shellClients, c); } ); connect(waylandServer(), &WaylandServer::shellClientRemoved, this, [this] (ShellClient *c) { - remove(s_waylandInternalId -1, m_internalClients, c); remove(s_waylandClientId -1, m_shellClients, c); } ); @@ -853,6 +845,20 @@ remove(s_x11UnmanagedId -1, m_unmanageds, u); } ); + const auto internals = workspace()->internalClients(); + for (auto c : internals) { + m_internalClients.append(c); + } + connect(workspace(), &Workspace::internalClientAdded, this, + [this](InternalClient *client) { + add(s_waylandInternalId -1, m_internalClients, client); + } + ); + connect(workspace(), &Workspace::internalClientRemoved, this, + [this](InternalClient *client) { + remove(s_waylandInternalId -1, m_internalClients, client); + } + ); } DebugConsoleModel::~DebugConsoleModel() = default; @@ -1110,7 +1116,7 @@ } if (ShellClient *c = shellClient(index)) { return propertyData(c, index, role); - } else if (ShellClient *c = internalClient(index)) { + } else if (InternalClient *c = internalClient(index)) { return propertyData(c, index, role); } else if (Client *c = x11Client(index)) { return propertyData(c, index, role); @@ -1161,7 +1167,7 @@ return clientForIndex(index, m_shellClients, s_waylandClientId); } -ShellClient *DebugConsoleModel::internalClient(const QModelIndex &index) const +InternalClient *DebugConsoleModel::internalClient(const QModelIndex &index) const { return clientForIndex(index, m_internalClients, s_waylandInternalId); } @@ -1207,9 +1213,6 @@ connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); } if (waylandServer()) { - for (auto c : waylandServer()->internalClients()) { - connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); - } connect(waylandServer(), &WaylandServer::shellClientAdded, this, [this, reset] (ShellClient *c) { connect(c->surface(), &SurfaceInterface::subSurfaceTreeChanged, this, reset); @@ -1235,6 +1238,8 @@ } ); connect(workspace(), &Workspace::unmanagedRemoved, this, reset); + connect(workspace(), &Workspace::internalClientAdded, this, reset); + connect(workspace(), &Workspace::internalClientRemoved, this, reset); } SurfaceTreeModel::~SurfaceTreeModel() = default; @@ -1255,12 +1260,11 @@ } return 0; } - const int internalClientsCount = waylandServer() ? waylandServer()->internalClients().count() : 0; // toplevel are all windows return workspace()->allClientList().count() + workspace()->desktopList().count() + workspace()->unmanagedList().count() + - internalClientsCount; + workspace()->internalClients().count(); } QModelIndex SurfaceTreeModel::index(int row, int column, const QModelIndex &parent) const @@ -1297,11 +1301,9 @@ return createIndex(row, column, unmanaged.at(row-reference)->surface()); } reference += unmanaged.count(); - if (waylandServer()) { - const auto &internal = waylandServer()->internalClients(); - if (row < reference + internal.count()) { - return createIndex(row, column, internal.at(row-reference)->surface()); - } + const auto &internal = workspace()->internalClients(); + if (row < reference + internal.count()) { + return createIndex(row, column, internal.at(row-reference)->surface()); } // not found return QModelIndex(); @@ -1359,14 +1361,6 @@ } } row += unmanaged.count(); - if (waylandServer()) { - const auto &internal = waylandServer()->internalClients(); - for (int i = 0; i < internal.count(); i++) { - if (internal.at(i)->surface() == parent) { - return createIndex(row + i, 0, parent); - } - } - } } return QModelIndex(); } diff --git a/effects.h b/effects.h --- a/effects.h +++ b/effects.h @@ -293,6 +293,7 @@ void slotClientShown(KWin::Toplevel*); void slotShellClientShown(KWin::Toplevel*); void slotUnmanagedShown(KWin::Toplevel*); + void slotInternalClientShown(KWin::Toplevel *client); void slotWindowClosed(KWin::Toplevel *c, KWin::Deleted *d); void slotClientMaximized(KWin::AbstractClient *c, MaximizeMode maxMode); void slotOpacityChanged(KWin::Toplevel *t, qreal oldOpacity); diff --git a/effects.cpp b/effects.cpp --- a/effects.cpp +++ b/effects.cpp @@ -30,6 +30,7 @@ #include "client.h" #include "cursor.h" #include "group.h" +#include "internal_client.h" #include "osd.h" #include "pointer_input.h" #include "unmanaged.h" @@ -180,6 +181,15 @@ connect(u, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotUnmanagedShown); } ); + connect(ws, &Workspace::internalClientAdded, this, + [this](InternalClient *client) { + if (client->readyForPainting()) { + slotInternalClientShown(client); + } else { + connect(client, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotInternalClientShown); + } + } + ); connect(ws, &Workspace::clientActivated, this, [this](KWin::AbstractClient *c) { emit windowActivated(c ? c->effectWindow() : nullptr); @@ -247,6 +257,10 @@ for (Unmanaged *u : ws->unmanagedList()) { setupUnmanagedConnections(u); } + const InternalClientList internalClients = ws->internalClients(); + for (InternalClient *client : internalClients) { + setupAbstractClientConnections(client); + } if (auto w = waylandServer()) { connect(w, &WaylandServer::shellClientAdded, this, [this](ShellClient *c) { @@ -582,6 +596,13 @@ emit windowAdded(u->effectWindow()); } +void EffectsHandlerImpl::slotInternalClientShown(KWin::Toplevel *toplevel) +{ + InternalClient *client = static_cast(toplevel); + setupAbstractClientConnections(client); + emit windowAdded(client->effectWindow()); +} + void EffectsHandlerImpl::slotWindowClosed(KWin::Toplevel *c, KWin::Deleted *d) { c->disconnect(this); @@ -1090,13 +1111,8 @@ EffectWindow *EffectsHandlerImpl::findWindow(QWindow *w) const { - if (waylandServer()) { - if (auto c = waylandServer()->findClient(w)) { - return c->effectWindow(); - } - } - if (auto u = Workspace::self()->findUnmanaged(w->winId())) { - return u->effectWindow(); + if (Toplevel *toplevel = workspace()->findInternal(w)) { + return toplevel->effectWindow(); } return nullptr; } @@ -1965,7 +1981,7 @@ QWindow *EffectWindowImpl::internalWindow() const { - auto client = qobject_cast(toplevel); + auto client = qobject_cast(toplevel); if (!client) { return nullptr; } diff --git a/geometry.cpp b/geometry.cpp --- a/geometry.cpp +++ b/geometry.cpp @@ -39,6 +39,7 @@ #include "screens.h" #include "effects.h" #include "screenedge.h" +#include "internal_client.h" #include #include #include @@ -200,7 +201,7 @@ } } if (waylandServer()) { - auto updateStrutsForWaylandClient = [&] (ShellClient *c) { + auto updateStrutsForWaylandClient = [&] (AbstractClient *c) { // assuming that only docks have "struts" and that all docks have a strut if (!c->hasStrut()) { return; @@ -268,9 +269,10 @@ for (auto c : clients) { updateStrutsForWaylandClient(c); } - const auto internalClients = waylandServer()->internalClients(); - for (auto c : internalClients) { - updateStrutsForWaylandClient(c); + // FIXME: Update struts on X11 too. + const InternalClientList internalClients = workspace()->internalClients(); + for (InternalClient *client : internalClients) { + updateStrutsForWaylandClient(client); } } #if 0 diff --git a/input.cpp b/input.cpp --- a/input.cpp +++ b/input.cpp @@ -46,6 +46,7 @@ #include "shell_client.h" #include "wayland_server.h" #include "xwl/xwayland_interface.h" +#include "internal_client.h" #include #include #include @@ -818,7 +819,7 @@ { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: { - auto s = waylandServer()->findClient(internal); + auto s = static_cast(workspace()->findInternal(internal)); if (s && s->isDecorated()) { // only perform mouse commands on decorated internal windows const auto actionResult = performClientMouseAction(event, s); @@ -845,7 +846,7 @@ return false; } if (event->angleDelta().y() != 0) { - auto s = waylandServer()->findClient(internal); + auto s = static_cast(workspace()->findInternal(internal)); if (s && s->isDecorated()) { // client window action only on vertical scrolling const auto actionResult = performClientWheelAction(event, s); @@ -868,7 +869,7 @@ return e.isAccepted(); } bool keyEvent(QKeyEvent *event) override { - const auto &internalClients = waylandServer()->internalClients(); + const InternalClientList &internalClients = workspace()->internalClients(); if (internalClients.isEmpty()) { return false; } @@ -2349,7 +2350,7 @@ const auto pos = position().toPoint(); internalWindow = findInternalWindow(pos); if (internalWindow) { - toplevel = waylandServer()->findClient(internalWindow); + toplevel = workspace()->findInternal(internalWindow); } else { toplevel = input()->findToplevel(pos); } @@ -2394,7 +2395,7 @@ return nullptr; } - const auto &internalClients = waylandServer()->internalClients(); + const InternalClientList &internalClients = workspace()->internalClients(); if (internalClients.isEmpty()) { return nullptr; } diff --git a/internal_client.h b/internal_client.h --- a/internal_client.h +++ b/internal_client.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2019 Martin Flöser +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,31 +20,41 @@ *********************************************************************/ #pragma once -#include "shell_client.h" - +#include "abstract_client.h" namespace KWin { -class KWIN_EXPORT InternalClient : public ShellClient +class KWIN_EXPORT InternalClient : public AbstractClient { Q_OBJECT + public: - InternalClient(KWayland::Server::ShellSurfaceInterface *surface); - // needed for template void WaylandServer::createSurface(T *surface) - InternalClient(KWayland::Server::XdgShellSurfaceInterface *surface); - // needed for template void WaylandServer::createSurface(T *surface) - InternalClient(KWayland::Server::XdgShellPopupInterface *surface); + explicit InternalClient(QWindow *window); ~InternalClient() override; bool eventFilter(QObject *watched, QEvent *event) override; + void blockActivityUpdates(bool b = true) override; + QStringList activities() const override; + QPoint clientContentPos() const override; + QSize clientSize() const override; + QRect transparentRect() const override; NET::WindowType windowType(bool direct = false, int supported_types = 0) const override; + void debug(QDebug &stream) const override; + double opacity() const override; + void setOpacity(double opacity) override; void killWindow() override; bool isPopupWindow() const override; void setInternalFramebufferObject(const QSharedPointer &fbo) override; + void setInternalImageObject(const QImage &image) override; + QString captionNormal() const override; + QString captionSuffix() const override; + QByteArray windowRole() const override; void closeWindow() override; bool isCloseable() const override; + bool isFullScreenable() const override; + bool isFullScreen() const override; bool isMaximizable() const override; bool isMinimizable() const override; bool isMovable() const override; @@ -57,27 +68,61 @@ bool isInputMethod() const override; bool isOutline() const override; quint32 windowId() const override; + MaximizeMode maximizeMode() const override; + QRect geometryRestore() const override; + bool isShown(bool shaded_is_shown) const override; + bool isHiddenInternal() const override; + void hideClient(bool hide) override; using AbstractClient::resizeWithChecks; void resizeWithChecks(int w, int h, ForceGeometry_t force = NormalGeometrySet) override; - QWindow *internalWindow() const override; + using AbstractClient::setGeometry; + void setGeometry(int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet) override; + void setGeometryRestore(const QRect &geo) override; bool supportsWindowRules() const override; + AbstractClient *findModal(bool allow_itself = false) override; + void setOnAllActivities(bool set) override; + void takeFocus() override; + bool userCanSetFullScreen() const override; + void setFullScreen(bool set, bool user = true) override; + void setNoBorder(bool set) override; + void updateDecoration(bool check_workspace_pos, bool force = false) override; + void updateColorScheme() override; + void showOnScreenEdge() override; + + void destroyClient(); + QWindow *internalWindow() const; protected: bool acceptsFocus() const override; + bool belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const override; + void changeMaximize(bool horizontal, bool vertical, bool adjust) override; void doMove(int x, int y) override; void doResizeSync() override; - bool requestGeometry(const QRect &rect) override; - void doSetGeometry(const QRect &rect) override; + void doSetGeometry(const QRect &rect); + void requestGeometry(const QRect &rect); + void updateCaption() override; private: - void findInternalWindow(); + QRect contentToFrame(const QRect &rect) const; + QRect frameToContent(const QRect &rect) const; + void map(QSharedPointer fbo); + void map(const QImage &image); + void unmap(); + void markAsMapped(); void updateInternalWindowGeometry(); void syncGeometryToInternalWindow(); + QWindow *m_internalWindow = nullptr; + QSize m_clientSize = QSize(0, 0); + QString m_captionNormal; + QString m_captionSuffix; + double m_opacity = 1.0; NET::WindowType m_windowType = NET::Normal; quint32 m_windowId = 0; - QWindow *m_internalWindow = nullptr; Qt::WindowFlags m_internalWindowFlags = Qt::WindowFlags(); + bool m_isUnmapped = true; + + Q_DISABLE_COPY(InternalClient) }; } diff --git a/internal_client.cpp b/internal_client.cpp --- a/internal_client.cpp +++ b/internal_client.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2019 Martin Flöser +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,72 +19,50 @@ along with this program. If not, see . *********************************************************************/ #include "internal_client.h" +#include "deleted.h" #include "workspace.h" -#include -#include - #include +#include Q_DECLARE_METATYPE(NET::WindowType) static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); namespace KWin { -InternalClient::InternalClient(KWayland::Server::ShellSurfaceInterface *surface) - : ShellClient(surface) +InternalClient::InternalClient(QWindow *window) + : m_internalWindow(window) + , m_windowId(window->winId()) + , m_internalWindowFlags(window->flags()) { - findInternalWindow(); + connect(m_internalWindow, &QWindow::xChanged, this, &InternalClient::updateInternalWindowGeometry); + connect(m_internalWindow, &QWindow::yChanged, this, &InternalClient::updateInternalWindowGeometry); + connect(m_internalWindow, &QWindow::widthChanged, this, &InternalClient::updateInternalWindowGeometry); + connect(m_internalWindow, &QWindow::heightChanged, this, &InternalClient::updateInternalWindowGeometry); + connect(m_internalWindow, &QWindow::opacityChanged, this, &InternalClient::setOpacity); + connect(m_internalWindow, &QWindow::destroyed, this, &InternalClient::destroyClient); + + const QVariant windowType = m_internalWindow->property("kwin_windowType"); + if (!windowType.isNull()) { + m_windowType = windowType.value(); + } + + setOnAllDesktops(true); + setOpacity(m_internalWindow->opacity()); + setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); + updateInternalWindowGeometry(); updateDecoration(true); -} -InternalClient::InternalClient(KWayland::Server::XdgShellSurfaceInterface *surface) - : ShellClient(surface) -{ -} + setupCompositing(); -InternalClient::InternalClient(KWayland::Server::XdgShellPopupInterface *surface) - : ShellClient(surface) -{ + m_internalWindow->installEventFilter(this); } -InternalClient::~InternalClient() = default; - -void InternalClient::findInternalWindow() +InternalClient::~InternalClient() { - const QWindowList windows = kwinApp()->topLevelWindows(); - for (QWindow *w: windows) { - auto s = KWayland::Client::Surface::fromWindow(w); - if (!s) { - continue; - } - if (s->id() != surface()->id()) { - continue; - } - m_internalWindow = w; - m_windowId = m_internalWindow->winId(); - m_internalWindowFlags = m_internalWindow->flags(); - connect(m_internalWindow, &QWindow::xChanged, this, &InternalClient::updateInternalWindowGeometry); - connect(m_internalWindow, &QWindow::yChanged, this, &InternalClient::updateInternalWindowGeometry); - connect(m_internalWindow, &QWindow::destroyed, this, [this] { m_internalWindow = nullptr; }); - connect(m_internalWindow, &QWindow::opacityChanged, this, &InternalClient::setOpacity); - - const QVariant windowType = m_internalWindow->property("kwin_windowType"); - if (!windowType.isNull()) { - m_windowType = windowType.value(); - } - setOpacity(m_internalWindow->opacity()); - - // skip close animation support - setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); - m_internalWindow->installEventFilter(this); - return; - } - - qCWarning(KWIN_CORE, "Couldn't find an internal window for surface with id %x", surface()->id()); } bool InternalClient::eventFilter(QObject *watched, QEvent *event) @@ -101,38 +80,106 @@ return false; } +void InternalClient::blockActivityUpdates(bool b) +{ + Q_UNUSED(b) + + // Internal clients do not support activities. +} + +QStringList InternalClient::activities() const +{ + return QStringList(); +} + +QPoint InternalClient::clientContentPos() const +{ + return -1 * clientPos(); +} + +QSize InternalClient::clientSize() const +{ + return m_clientSize; +} + +QRect InternalClient::transparentRect() const +{ + return QRect(); +} + NET::WindowType InternalClient::windowType(bool direct, int supported_types) const { Q_UNUSED(direct) Q_UNUSED(supported_types) return m_windowType; } +void InternalClient::debug(QDebug &stream) const +{ + stream.nospace() << "\'InternalClient:" << m_internalWindow << "\'"; +} + +double InternalClient::opacity() const +{ + return m_opacity; +} + +void InternalClient::setOpacity(double opacity) +{ + if (m_opacity == opacity) { + return; + } + + const double oldOpacity = m_opacity; + m_opacity = opacity; + + emit opacityChanged(this, oldOpacity); +} + void InternalClient::killWindow() { - // we don't kill our internal windows + // We don't kill our internal windows. } bool InternalClient::isPopupWindow() const { - if (Toplevel::isPopupWindow()) { + if (AbstractClient::isPopupWindow()) { return true; } return m_internalWindowFlags.testFlag(Qt::Popup); } void InternalClient::setInternalFramebufferObject(const QSharedPointer &fbo) { - if (fbo.isNull()) { + if (!fbo.isNull()) { + map(fbo); + } else { unmap(); - return; } +} - setClientSize(fbo->size() / surface()->scale()); - markAsMapped(); - doSetGeometry(QRect(geom.topLeft(), clientSize())); - Toplevel::setInternalFramebufferObject(fbo); - Toplevel::addDamage(QRegion(0, 0, width(), height())); +void InternalClient::setInternalImageObject(const QImage &image) +{ + if (!image.isNull()) { + map(image); + } else { + unmap(); + } +} + +QString InternalClient::captionNormal() const +{ + return m_captionNormal; +} + +QString InternalClient::captionSuffix() const +{ + return m_captionSuffix; +} + +QByteArray InternalClient::windowRole() const +{ + return QByteArray(); } void InternalClient::closeWindow() @@ -147,6 +194,16 @@ return true; } +bool InternalClient::isFullScreenable() const +{ + return false; +} + +bool InternalClient::isFullScreen() const +{ + return false; +} + bool InternalClient::isMaximizable() const { return false; @@ -187,11 +244,6 @@ return false; } -bool InternalClient::acceptsFocus() const -{ - return false; -} - bool InternalClient::isInternal() const { return true; @@ -226,111 +278,345 @@ return m_windowId; } -void InternalClient::updateInternalWindowGeometry() +MaximizeMode InternalClient::maximizeMode() const +{ + return MaximizeRestore; +} + +QRect InternalClient::geometryRestore() const { + return QRect(); +} + +bool InternalClient::isShown(bool shaded_is_shown) const +{ + Q_UNUSED(shaded_is_shown) + + return !m_isUnmapped; +} + +bool InternalClient::isHiddenInternal() const +{ + return m_isUnmapped; +} + +void InternalClient::hideClient(bool hide) +{ + // FIXME +} + +void InternalClient::resizeWithChecks(int w, int h, ForceGeometry_t force) +{ + Q_UNUSED(force) if (!m_internalWindow) { return; } - doSetGeometry(QRect(m_internalWindow->geometry().topLeft() - QPoint(borderLeft(), borderTop()), - m_internalWindow->geometry().size() + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); + QRect area = workspace()->clientArea(WorkArea, this); + // don't allow growing larger than workarea + if (w > area.width()) { + w = area.width(); + } + if (h > area.height()) { + h = area.height(); + } + m_internalWindow->setGeometry(frameToContent(area)); } -bool InternalClient::requestGeometry(const QRect &rect) +void InternalClient::setGeometry(int x, int y, int w, int h, ForceGeometry_t force) { - if (!ShellClient::requestGeometry(rect)) { - return false; + if (areGeometryUpdatesBlocked()) { + // when the GeometryUpdateBlocker exits the current geom is passed to setGeometry + // thus we need to set it here. + geom = QRect(x, y, w, h); + if (pendingGeometryUpdate() == PendingGeometryForced) { + // Maximum, nothing needed. + } else if (force == ForceGeometrySet) { + setPendingGeometryUpdate(PendingGeometryForced); + } else { + setPendingGeometryUpdate(PendingGeometryNormal); + } + return; } - if (m_internalWindow) { - m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); + + if (pendingGeometryUpdate() != PendingGeometryNone) { + // Reset geometry to the one before blocking, so that we can compare properly. + geom = geometryBeforeUpdateBlocking(); } - return true; + + const QRect windowRect = frameToContent(geom); + + if (windowRect.size() == m_clientSize) { + doSetGeometry(geom); + } else { + requestGeometry(geom); + } +} + +void InternalClient::setGeometryRestore(const QRect &geo) +{ + Q_UNUSED(geo) + + // Internal clients are not maximizable. +} + +bool InternalClient::supportsWindowRules() const +{ + return false; +} + +AbstractClient *InternalClient::findModal(bool allow_itself) +{ + Q_UNUSED(allow_itself) + return nullptr; +} + +void InternalClient::setOnAllActivities(bool set) +{ + Q_UNUSED(set) + + // Internal clients do not support activities. +} + +void InternalClient::takeFocus() +{ + // FIXME +} + +bool InternalClient::userCanSetFullScreen() const +{ + // FIXME + return false; +} + +void InternalClient::setFullScreen(bool set, bool user) +{ + // FIXME +} + +void InternalClient::setNoBorder(bool set) +{ + // FIXME +} + +void InternalClient::updateDecoration(bool check_workspace_pos, bool force) +{ + // FIXME +} + +void InternalClient::updateColorScheme() +{ + // FIXME +} + +void InternalClient::showOnScreenEdge() +{ + // FIXME +} + +QWindow *InternalClient::internalWindow() const +{ + return m_internalWindow; +} + +bool InternalClient::acceptsFocus() const +{ + return false; +} + +bool InternalClient::belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const +{ + Q_UNUSED(checks) + + return qobject_cast(other) != nullptr; +} + +void InternalClient::doMove(int x, int y) +{ + Q_UNUSED(x) + Q_UNUSED(y) + + syncGeometryToInternalWindow(); +} + +void InternalClient::changeMaximize(bool horizontal, bool vertical, bool adjust) +{ + Q_UNUSED(horizontal) + Q_UNUSED(vertical) + Q_UNUSED(adjust) + + // Internal clients are not maximizable. +} + +void InternalClient::doResizeSync() +{ + requestGeometry(moveResizeGeometry()); } void InternalClient::doSetGeometry(const QRect &rect) { if (geom == rect && pendingGeometryUpdate() == PendingGeometryNone) { return; } - if (!isUnmapped()) { + + if (m_isUnmapped) { addWorkspaceRepaint(visibleRect()); } + geom = rect; - if (isUnmapped() && geometryRestore().isEmpty() && !geom.isEmpty()) { + if (m_isUnmapped && geometryRestore().isEmpty() && !geom.isEmpty()) { // use first valid geometry as restore geometry setGeometryRestore(geom); } - if (!isUnmapped()) { + if (!m_isUnmapped) { addWorkspaceRepaint(visibleRect()); } + syncGeometryToInternalWindow(); + if (hasStrut()) { workspace()->updateClientArea(); } - const auto old = geometryBeforeUpdateBlocking(); + + const QRect old = geometryBeforeUpdateBlocking(); updateGeometryBeforeUpdateBlocking(); emit geometryShapeChanged(this, old); if (isResize()) { performMoveResize(); } } -void InternalClient::doMove(int x, int y) +void InternalClient::requestGeometry(const QRect &rect) { - Q_UNUSED(x) - Q_UNUSED(y) - syncGeometryToInternalWindow(); + if (m_internalWindow) { + m_internalWindow->setGeometry(frameToContent(rect)); + } } -void InternalClient::syncGeometryToInternalWindow() +void InternalClient::updateCaption() { - if (!m_internalWindow) { - return; + // FIXME +} + +void InternalClient::destroyClient() +{ + if (isMoveResize()) { + leaveMoveResize(); + } + + Deleted *deleted = nullptr; + if (workspace()) { + deleted = Deleted::create(this); } - const QRect windowRect = QRect(geom.topLeft() + QPoint(borderLeft(), borderTop()), - geom.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom())); - if (m_internalWindow->geometry() != windowRect) { - // delay to end of cycle to prevent freeze, see BUG 384441 - QTimer::singleShot(0, m_internalWindow, std::bind(static_cast(&QWindow::setGeometry), m_internalWindow, windowRect)); + + emit windowClosed(this, deleted); + + destroyDecoration(); + + workspace()->removeInternalClient(this); + + if (deleted) { + deleted->unrefWindow(); } + + m_internalWindow = nullptr; + + delete this; } -void InternalClient::resizeWithChecks(int w, int h, ForceGeometry_t force) +QRect InternalClient::contentToFrame(const QRect &rect) const { - Q_UNUSED(force) - if (!m_internalWindow) { + return rect + frameMargins(); +} + +QRect InternalClient::frameToContent(const QRect &rect) const +{ + return rect - frameMargins(); +} + +void InternalClient::markAsMapped() +{ + if (!m_isUnmapped) { return; } - QRect area = workspace()->clientArea(WorkArea, this); - // don't allow growing larger than workarea - if (w > area.width()) { - w = area.width(); - } - if (h > area.height()) { - h = area.height(); + + if (!ready_for_painting) { + setReadyForPainting(); + } else { + addRepaintFull(); + emit windowShown(this); } - m_internalWindow->setGeometry(QRect(pos() + QPoint(borderLeft(), borderTop()), QSize(w, h) - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); + + m_isUnmapped = false; + + // TODO: See if there's better place when this method should be invoked. + workspace()->addInternalClient(this); } -void InternalClient::doResizeSync() +void InternalClient::map(QSharedPointer fbo) { - if (!m_internalWindow) { - return; + // TODO: Retrieve buffer device pixel ratio. + + m_clientSize = fbo->size(); + + markAsMapped(); + + doSetGeometry(QRect(geom.topLeft(), m_clientSize)); + + Toplevel::setInternalFramebufferObject(fbo); + Toplevel::addDamage(QRegion(0, 0, width(), height())); +} + +void InternalClient::map(const QImage &image) +{ + const qreal devicePixelRatio = image.devicePixelRatio(); + + m_clientSize = image.size() / devicePixelRatio; + + markAsMapped(); + + doSetGeometry(QRect(geom.topLeft(), m_clientSize)); + + Toplevel::setInternalImageObject(image); + Toplevel::addDamage(QRegion(0, 0, width(), height())); +} + +void InternalClient::unmap() +{ + m_isUnmapped = true; + + if (isMoveResize()) { + leaveMoveResize(); + } + + if (workspace()) { + addWorkspaceRepaint(visibleRect()); + workspace()->clientHidden(this); } - const auto rect = moveResizeGeometry(); - m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); + + emit windowHidden(this); } -QWindow *InternalClient::internalWindow() const +void InternalClient::updateInternalWindowGeometry() { - return m_internalWindow; + if (m_internalWindow) { + doSetGeometry(contentToFrame(m_internalWindow->geometry())); + } } -bool InternalClient::supportsWindowRules() const +void InternalClient::syncGeometryToInternalWindow() { - return false; + if (!m_internalWindow) { + return; + } + + if (contentToFrame(m_internalWindow->geometry()) == geometry()) { + return; + } + + QTimer::singleShot(0, this, [this] { requestGeometry(geometry()); }); } } diff --git a/layers.cpp b/layers.cpp --- a/layers.cpp +++ b/layers.cpp @@ -95,6 +95,7 @@ #include "screenedge.h" #include "shell_client.h" #include "wayland_server.h" +#include "internal_client.h" #include @@ -764,14 +765,14 @@ } } } - if (waylandServer()) { - const auto clients = waylandServer()->internalClients(); - for (auto c: clients) { - if (c->isShown(false)) { - x_stacking << c; - } + + const InternalClientList clients = workspace()->internalClients(); + for (InternalClient *client : clients) { + if (client->isShown(false)) { + x_stacking.append(client); } } + m_xStackingDirty = false; } diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.h b/platformsupport/scenes/opengl/abstract_egl_backend.h --- a/platformsupport/scenes/opengl/abstract_egl_backend.h +++ b/platformsupport/scenes/opengl/abstract_egl_backend.h @@ -110,8 +110,10 @@ private: bool loadShmTexture(const QPointer &buffer); bool loadEglTexture(const QPointer &buffer); + bool loadInternalImageObject(WindowPixmap *pixmap); EGLImageKHR attach(const QPointer &buffer); bool updateFromFBO(const QSharedPointer &fbo); + bool updateFromInternalImageObject(WindowPixmap *pixmap); SceneOpenGLTexture *q; AbstractEglBackend *m_backend; EGLImageKHR m_image; diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/platformsupport/scenes/opengl/abstract_egl_backend.cpp --- a/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -341,11 +341,16 @@ bool AbstractEglTexture::loadTexture(WindowPixmap *pixmap) { + // FIXME: Refactor this method, it's a huge mess. + const auto &buffer = pixmap->buffer(); if (buffer.isNull()) { if (updateFromFBO(pixmap->fbo())) { return true; } + if (loadInternalImageObject(pixmap)) { + return true; + } return false; } // try Wayland loading @@ -361,13 +366,14 @@ void AbstractEglTexture::updateTexture(WindowPixmap *pixmap) { + // FIXME: Refactor this method, it's a huge mess. + const auto &buffer = pixmap->buffer(); if (buffer.isNull()) { - const auto &fbo = pixmap->fbo(); - if (!fbo.isNull()) { - if (m_texture != fbo->texture()) { - updateFromFBO(fbo); - } + if (updateFromFBO(pixmap->fbo())) { + return; + } + if (updateFromInternalImageObject(pixmap)) { return; } return; @@ -504,6 +510,58 @@ return true; } +bool AbstractEglTexture::loadInternalImageObject(WindowPixmap *pixmap) +{ + // FIXME: Lame! Lame! Lame! Lame! Lame! Lame! + + const QImage image = pixmap->internalImage(); + if (image.isNull()) { + return false; + } + + glGenTextures(1, &m_texture); + q->setFilter(GL_LINEAR); + q->setWrapMode(GL_CLAMP_TO_EDGE); + q->setYInverted(true); + q->bind(); + + const QSize &size = image.size(); + // TODO: this should be shared with GLTexture(const QImage&, GLenum) + GLenum format = 0; + switch (image.format()) { + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + format = GL_RGBA8; + break; + case QImage::Format_RGB32: + format = GL_RGB8; + break; + default: + return false; + } + if (GLPlatform::instance()->isGLES()) { + if (s_supportsARGB32 && format == GL_RGBA8) { + const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + glTexImage2D(m_target, 0, GL_BGRA_EXT, im.width(), im.height(), + 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.bits()); + } else { + const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + glTexImage2D(m_target, 0, GL_RGBA, im.width(), im.height(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, im.bits()); + } + } else { + glTexImage2D(m_target, 0, format, size.width(), size.height(), 0, + GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); + } + + q->unbind(); + + m_size = size; + updateMatrix(); + + return true; +} + EGLImageKHR AbstractEglTexture::attach(const QPointer< KWayland::Server::BufferInterface > &buffer) { EGLint format, yInverted; @@ -546,5 +604,55 @@ return true; } +bool AbstractEglTexture::updateFromInternalImageObject(WindowPixmap *pixmap) +{ + // FIXME: Lame! Lame! Lame! Lame! Lame! Lame! + + const QImage image = pixmap->internalImage(); + if (image.isNull()) { + return false; + } + + if (m_size != image.size()) { + glDeleteTextures(1, &m_texture); + return loadInternalImageObject(pixmap); + } + + const QRegion damage = pixmap->toplevel()->damage(); + const qreal scale = image.devicePixelRatio(); + + q->bind(); + + // TODO: this should be shared with GLTexture::update + if (GLPlatform::instance()->isGLES()) { + if (s_supportsARGB32 && (image.format() == QImage::Format_ARGB32 || image.format() == QImage::Format_ARGB32_Premultiplied)) { + const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + for (const QRect &rect : damage) { + auto scaledRect = QRect(rect.x() * scale, rect.y() * scale, rect.width() * scale, rect.height() * scale); + glTexSubImage2D(m_target, 0, scaledRect.x(), scaledRect.y(), scaledRect.width(), scaledRect.height(), + GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.copy(scaledRect).bits()); + } + } else { + const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + for (const QRect &rect : damage) { + auto scaledRect = QRect(rect.x() * scale, rect.y() * scale, rect.width() * scale, rect.height() * scale); + glTexSubImage2D(m_target, 0, scaledRect.x(), scaledRect.y(), scaledRect.width(), scaledRect.height(), + GL_RGBA, GL_UNSIGNED_BYTE, im.copy(scaledRect).bits()); + } + } + } else { + const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + for (const QRect &rect : damage) { + auto scaledRect = QRect(rect.x() * scale, rect.y() * scale, rect.width() * scale, rect.height() * scale); + glTexSubImage2D(m_target, 0, scaledRect.x(), scaledRect.y(), scaledRect.width(), scaledRect.height(), + GL_BGRA, GL_UNSIGNED_BYTE, im.copy(scaledRect).bits()); + } + } + + q->unbind(); + + return true; +} + } diff --git a/plugins/qpa/CMakeLists.txt b/plugins/qpa/CMakeLists.txt --- a/plugins/qpa/CMakeLists.txt +++ b/plugins/qpa/CMakeLists.txt @@ -7,7 +7,6 @@ eglhelpers.cpp integration.cpp main.cpp - nativeinterface.cpp offscreensurface.cpp platformcursor.cpp screen.cpp @@ -29,7 +28,6 @@ target_link_libraries(KWinQpaPlugin kwin - KF5::WaylandClient ${QT5PLATFORMSUPPORT_LIBS} Fontconfig::Fontconfig ${FREETYPE_LIBRARIES} diff --git a/plugins/qpa/backingstore.h b/plugins/qpa/backingstore.h --- a/plugins/qpa/backingstore.h +++ b/plugins/qpa/backingstore.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,37 +23,23 @@ #include -namespace KWayland -{ -namespace Client -{ -class Buffer; -class ShmPool; -} -} - namespace KWin { namespace QPA { class BackingStore : public QPlatformBackingStore { public: - explicit BackingStore(QWindow *w, KWayland::Client::ShmPool *shm); + explicit BackingStore(QWindow *window); ~BackingStore() override; QPaintDevice *paintDevice() override; void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override; void resize(const QSize &size, const QRegion &staticContents) override; - void beginPaint(const QRegion &) override; private: - int scale() const; - KWayland::Client::ShmPool *m_shm; - QWeakPointer m_buffer; QImage m_backBuffer; - QSize m_size; }; } diff --git a/plugins/qpa/backingstore.cpp b/plugins/qpa/backingstore.cpp --- a/plugins/qpa/backingstore.cpp +++ b/plugins/qpa/backingstore.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,39 +18,21 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ -#include "window.h" #include "backingstore.h" -#include "../../wayland_server.h" +#include "window.h" -#include -#include -#include -#include +#include "internal_client.h" + +#include namespace KWin { namespace QPA { -BackingStore::BackingStore(QWindow *w, KWayland::Client::ShmPool *shm) - : QPlatformBackingStore(w) - , m_shm(shm) - , m_backBuffer(QSize(), QImage::Format_ARGB32_Premultiplied) +BackingStore::BackingStore(QWindow *window) + : QPlatformBackingStore(window) { - QObject::connect(m_shm, &KWayland::Client::ShmPool::poolResized, - [this] { - if (!m_buffer) { - return; - } - auto b = m_buffer.toStrongRef(); - if (!b->isUsed()){ - return; - } - const QSize size = m_backBuffer.size(); - m_backBuffer = QImage(b->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); - m_backBuffer.setDevicePixelRatio(scale()); - } - ); } BackingStore::~BackingStore() = default; @@ -62,66 +45,30 @@ void BackingStore::resize(const QSize &size, const QRegion &staticContents) { Q_UNUSED(staticContents) - m_size = size * scale(); - if (!m_buffer) { + + if (m_backBuffer.size() == size) { return; } - m_buffer.toStrongRef()->setUsed(false); - m_buffer.clear(); + + const QPlatformWindow *platformWindow = static_cast(window()->handle()); + const qreal devicePixelRatio = platformWindow->devicePixelRatio(); + + m_backBuffer = QImage(size * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); + m_backBuffer.setDevicePixelRatio(devicePixelRatio); } void BackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { Q_UNUSED(region) Q_UNUSED(offset) - auto w = static_cast(window->handle()); - auto s = w->surface(); - if (!s) { + Window *platformWindow = static_cast(window->handle()); + InternalClient *client = platformWindow->client(); + if (!client) { return; } - s->attachBuffer(m_buffer); - // TODO: proper damage region - s->damage(QRect(QPoint(0, 0), m_backBuffer.size() / scale())); - s->commit(KWayland::Client::Surface::CommitFlag::None); - waylandServer()->internalClientConection()->flush(); - waylandServer()->dispatch(); -} -void BackingStore::beginPaint(const QRegion&) -{ - if (m_buffer) { - auto b = m_buffer.toStrongRef(); - if (b->isReleased()) { - // we can re-use this buffer - b->setReleased(false); - return; - } else { - // buffer is still in use, get a new one - b->setUsed(false); - } - } - auto oldBuffer = m_buffer.toStrongRef(); - m_buffer.clear(); - m_buffer = m_shm->getBuffer(m_size, m_size.width() * 4); - if (!m_buffer) { - m_backBuffer = QImage(); - return; - } - auto b = m_buffer.toStrongRef(); - b->setUsed(true); - m_backBuffer = QImage(b->address(), m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied); - m_backBuffer.setDevicePixelRatio(scale()); - if (oldBuffer) { - b->copy(oldBuffer->address()); - } else { - m_backBuffer.fill(Qt::transparent); - } -} - -int BackingStore::scale() const -{ - return static_cast(window()->handle())->scale(); + client->setInternalImageObject(m_backBuffer); } } diff --git a/plugins/qpa/integration.h b/plugins/qpa/integration.h --- a/plugins/qpa/integration.h +++ b/plugins/qpa/integration.h @@ -27,15 +27,6 @@ #include #include -namespace KWayland -{ -namespace Client -{ -class Compositor; -class Shell; -} -} - namespace KWin { namespace QPA @@ -58,24 +49,16 @@ QPlatformFontDatabase *fontDatabase() const override; QStringList themeNames() const override; QPlatformTheme *createPlatformTheme(const QString &name) const override; - QPlatformNativeInterface *nativeInterface() const override; QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; - - void initialize() override; QPlatformInputContext *inputContext() const override; - KWayland::Client::Compositor *compositor() const; - EGLDisplay eglDisplay() const; + void initialize() override; private: void initScreens(); - KWayland::Client::Shell *shell() const; QPlatformFontDatabase *m_fontDb; QPlatformNativeInterface *m_nativeInterface; - KWayland::Client::Compositor *m_compositor = nullptr; - KWayland::Client::Shell *m_shell = nullptr; - EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; Screen *m_dummyScreen = nullptr; QScopedPointer m_inputContext; QVector m_screens; diff --git a/plugins/qpa/integration.cpp b/plugins/qpa/integration.cpp --- a/plugins/qpa/integration.cpp +++ b/plugins/qpa/integration.cpp @@ -19,23 +19,14 @@ *********************************************************************/ #include "integration.h" #include "backingstore.h" -#include "nativeinterface.h" #include "offscreensurface.h" #include "screen.h" #include "sharingplatformcontext.h" #include "window.h" #include "../../main.h" #include "../../platform.h" #include "../../screens.h" #include "../../virtualkeyboard.h" -#include "../../wayland_server.h" - -#include -#include -#include -#include -#include -#include #include #include @@ -59,7 +50,6 @@ : QObject() , QPlatformIntegration() , m_fontDb(new QGenericUnixFontDatabase()) - , m_nativeInterface(new NativeInterface(this)) , m_inputContext() { } @@ -139,25 +129,12 @@ QPlatformBackingStore *Integration::createPlatformBackingStore(QWindow *window) const { - auto registry = waylandServer()->internalClientRegistry(); - const auto shm = registry->interface(KWayland::Client::Registry::Interface::Shm); - if (shm.name == 0u) { - return nullptr; - } - return new BackingStore(window, registry->createShmPool(shm.name, shm.version, window)); + return new BackingStore(window); } QPlatformWindow *Integration::createPlatformWindow(QWindow *window) const { - auto c = compositor(); - auto s = shell(); - if (!s || !c) { - return new QPlatformWindow(window); - } else { - // don't set window as parent, cause infinite recursion in PlasmaQuick::Dialog - auto surface = c->createSurface(c); - return new Window(window, surface, s->createSurface(surface, surface), this); - } + return new Window(window); } QPlatformOffscreenSurface *Integration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const @@ -183,11 +160,6 @@ return QStringList({QLatin1String(QGenericUnixTheme::name)}); } -QPlatformNativeInterface *Integration::nativeInterface() const -{ - return m_nativeInterface; -} - QPlatformOpenGLContext *Integration::createPlatformOpenGLContext(QOpenGLContext *context) const { if (kwinApp()->platform()->supportsQpaContext()) { @@ -235,37 +207,6 @@ m_screens = newScreens; } -KWayland::Client::Compositor *Integration::compositor() const -{ - if (!m_compositor) { - using namespace KWayland::Client; - auto registry = waylandServer()->internalClientRegistry(); - const auto c = registry->interface(Registry::Interface::Compositor); - if (c.name != 0u) { - const_cast(this)->m_compositor = registry->createCompositor(c.name, c.version, registry); - } - } - return m_compositor; -} - -KWayland::Client::Shell *Integration::shell() const -{ - if (!m_shell) { - using namespace KWayland::Client; - auto registry = waylandServer()->internalClientRegistry(); - const auto s = registry->interface(Registry::Interface::Shell); - if (s.name != 0u) { - const_cast(this)->m_shell = registry->createShell(s.name, s.version, registry); - } - } - return m_shell; -} - -EGLDisplay Integration::eglDisplay() const -{ - return m_eglDisplay; -} - QPlatformInputContext *Integration::inputContext() const { return m_inputContext.data(); diff --git a/plugins/qpa/nativeinterface.h b/plugins/qpa/nativeinterface.h deleted file mode 100644 --- a/plugins/qpa/nativeinterface.h +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 2015 Martin Gräßlin - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ -#ifndef KWIN_QPA_NATIVEINTERFACE_H -#define KWIN_QPA_NATIVEINTERFACE_H - -#include - -namespace KWin -{ -namespace QPA -{ - -class Integration; - -class NativeInterface : public QPlatformNativeInterface -{ -public: - explicit NativeInterface(Integration *integration); - void *nativeResourceForIntegration(const QByteArray &resource) override; - void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window) override; - QFunctionPointer platformFunction(const QByteArray &function) const override; - -private: - Integration *m_integration; -}; - -} -} - -#endif diff --git a/plugins/qpa/nativeinterface.cpp b/plugins/qpa/nativeinterface.cpp deleted file mode 100644 --- a/plugins/qpa/nativeinterface.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 2015 Martin Gräßlin - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ -#include "nativeinterface.h" -#include "integration.h" -#include "window.h" -#include "../../wayland_server.h" - -#include -#include -#include - -#include - -namespace KWin -{ -namespace QPA -{ - -static const QByteArray s_displayKey = QByteArrayLiteral("display"); -static const QByteArray s_wlDisplayKey = QByteArrayLiteral("wl_display"); -static const QByteArray s_compositorKey = QByteArrayLiteral("compositor"); -static const QByteArray s_surfaceKey = QByteArrayLiteral("surface"); - -NativeInterface::NativeInterface(Integration *integration) - : QPlatformNativeInterface() - , m_integration(integration) -{ -} - -void *NativeInterface::nativeResourceForIntegration(const QByteArray &resource) -{ - const QByteArray r = resource.toLower(); - if (r == s_displayKey || r == s_wlDisplayKey) { - if (!waylandServer() || !waylandServer()->internalClientConection()) { - return nullptr; - } - return waylandServer()->internalClientConection()->display(); - } - if (r == s_compositorKey) { - return static_cast(*m_integration->compositor()); - } - return nullptr; -} - -void *NativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) -{ - const QByteArray r = resource.toLower(); - if (r == s_displayKey || r == s_wlDisplayKey) { - if (!waylandServer() || !waylandServer()->internalClientConection()) { - return nullptr; - } - return waylandServer()->internalClientConection()->display(); - } - if (r == s_compositorKey) { - return static_cast(*m_integration->compositor()); - } - if (r == s_surfaceKey && window) { - if (auto handle = window->handle()) { - if (auto surface = static_cast(handle)->surface()) { - return static_cast(*surface); - } - } - } - return nullptr; -} - -static void roundtrip() -{ - if (!waylandServer()) { - return; - } - auto c = waylandServer()->internalClientConection(); - if (!c) { - return; - } - c->flush(); - waylandServer()->dispatch(); -} - -QFunctionPointer NativeInterface::platformFunction(const QByteArray &function) const -{ - if (qstrcmp(function.toLower(), "roundtrip") == 0) { - return &roundtrip; - } - return nullptr; -} - -} -} diff --git a/plugins/qpa/sharingplatformcontext.cpp b/plugins/qpa/sharingplatformcontext.cpp --- a/plugins/qpa/sharingplatformcontext.cpp +++ b/plugins/qpa/sharingplatformcontext.cpp @@ -20,8 +20,11 @@ #include "sharingplatformcontext.h" #include "offscreensurface.h" #include "window.h" + +#include "../../internal_client.h" +#include "../../main.h" #include "../../platform.h" -#include "../../shell_client.h" + #include #include @@ -82,14 +85,13 @@ { if (surface->surface()->surfaceClass() == QSurface::Window) { Window *window = static_cast(surface); - auto c = window->shellClient(); - if (!c) { - qCDebug(KWIN_QPA) << "SwapBuffers called but there is no ShellClient"; + InternalClient *client = window->client(); + if (!client) { return; } context()->makeCurrent(surface->surface()); glFlush(); - c->setInternalFramebufferObject(window->swapFBO()); + client->setInternalFramebufferObject(window->swapFBO()); window->bindContentFBO(); } } diff --git a/plugins/qpa/window.h b/plugins/qpa/window.h --- a/plugins/qpa/window.h +++ b/plugins/qpa/window.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,69 +21,44 @@ #ifndef KWIN_QPA_WINDOW_H #define KWIN_QPA_WINDOW_H -#include -#include "fixqopengl.h" - -#include #include class QOpenGLFramebufferObject; - -namespace KWayland -{ -namespace Client -{ -class Surface; -class ShellSurface; -} -} - namespace KWin { -class ShellClient; +class InternalClient; namespace QPA { -class Integration; - class Window : public QPlatformWindow { public: - explicit Window(QWindow *window, KWayland::Client::Surface *surface, KWayland::Client::ShellSurface *shellSurface, const Integration *integration); + explicit Window(QWindow *window); ~Window() override; void setVisible(bool visible) override; void setGeometry(const QRect &rect) override; WId winId() const override; - - KWayland::Client::Surface *surface() const { - return m_surface; - } - - int scale() const; qreal devicePixelRatio() const override; void bindContentFBO(); - const QSharedPointer &contentFBO() const { - return m_contentFBO; - } + const QSharedPointer &contentFBO() const; QSharedPointer swapFBO(); - ShellClient *shellClient(); + + InternalClient *client() const; private: - void unmap(); void createFBO(); + void map(); + void unmap(); - KWayland::Client::Surface *m_surface; - KWayland::Client::ShellSurface *m_shellSurface; + InternalClient *m_handle = nullptr; QSharedPointer m_contentFBO; - bool m_resized = false; - ShellClient *m_shellClient = nullptr; quint32 m_windowId; - const Integration *m_integration; + bool m_resized = false; int m_scale = 1; }; diff --git a/plugins/qpa/window.cpp b/plugins/qpa/window.cpp --- a/plugins/qpa/window.cpp +++ b/plugins/qpa/window.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,58 +19,41 @@ along with this program. If not, see . *********************************************************************/ #include "window.h" -#include "integration.h" #include "screens.h" -#include "../../shell_client.h" -#include "../../wayland_server.h" + +#include "internal_client.h" + #include #include #include -#include -#include -#include -#include - namespace KWin { namespace QPA { static quint32 s_windowId = 0; -Window::Window(QWindow *window, KWayland::Client::Surface *surface, KWayland::Client::ShellSurface *shellSurface, const Integration *integration) +Window::Window(QWindow *window) : QPlatformWindow(window) - , m_surface(surface) - , m_shellSurface(shellSurface) , m_windowId(++s_windowId) - , m_integration(integration) , m_scale(screens()->maxScale()) { - m_surface->setScale(m_scale); - - QObject::connect(m_surface, &QObject::destroyed, window, [this] { m_surface = nullptr;}); - QObject::connect(m_shellSurface, &QObject::destroyed, window, [this] { m_shellSurface = nullptr;}); - waylandServer()->internalClientConection()->flush(); } Window::~Window() { unmap(); - delete m_shellSurface; - delete m_surface; -} - -WId Window::winId() const -{ - return m_windowId; } void Window::setVisible(bool visible) { - if (!visible) { + if (visible) { + map(); + } else { unmap(); } + QPlatformWindow::setVisible(visible); } @@ -100,18 +84,14 @@ QWindowSystemInterface::handleGeometryChange(window(), geometry()); } -void Window::unmap() +WId Window::winId() const { - if (m_shellClient) { - m_shellClient->setInternalFramebufferObject(QSharedPointer()); - } - if (m_surface) { - m_surface->attachBuffer(KWayland::Client::Buffer::Ptr()); - m_surface->commit(KWayland::Client::Surface::CommitFlag::None); - } - if (waylandServer()->internalClientConection()) { - waylandServer()->internalClientConection()->flush(); - } + return m_windowId; +} + +qreal Window::devicePixelRatio() const +{ + return m_scale; } void Window::bindContentFBO() @@ -122,14 +102,23 @@ m_contentFBO->bind(); } +const QSharedPointer &Window::contentFBO() const +{ + return m_contentFBO; +} + QSharedPointer Window::swapFBO() { - auto fbo = m_contentFBO; + QSharedPointer fbo = m_contentFBO; m_contentFBO.clear(); - m_surface->commit(KWayland::Client::Surface::CommitFlag::None); return fbo; } +InternalClient *Window::client() const +{ + return m_handle; +} + void Window::createFBO() { const QRect &r = geometry(); @@ -144,23 +133,25 @@ m_resized = false; } -ShellClient *Window::shellClient() +void Window::map() { - if (!m_shellClient) { - waylandServer()->dispatch(); - m_shellClient = waylandServer()->findClient(window()); + if (m_handle) { + return; } - return m_shellClient; -} -int Window::scale() const -{ - return m_scale; + m_handle = new InternalClient(window()); } -qreal Window::devicePixelRatio() const +void Window::unmap() { - return m_scale; + if (!m_handle) { + return; + } + + m_handle->destroyClient(); + m_handle = nullptr; + + m_contentFBO = nullptr; } } diff --git a/scene.h b/scene.h --- a/scene.h +++ b/scene.h @@ -427,6 +427,7 @@ **/ QPointer buffer() const; const QSharedPointer &fbo() const; + QImage internalImage() const; /** * @brief Whether this WindowPixmap is considered as discarded. This means the window has changed in a way that a new * WindowPixmap should have been created already. @@ -514,6 +515,7 @@ QRect m_contentsRect; QPointer m_buffer; QSharedPointer m_fbo; + QImage m_internalImage; WindowPixmap *m_parent = nullptr; QVector m_children; QPointer m_subSurface; @@ -620,6 +622,12 @@ return m_fbo; } +inline +QImage WindowPixmap::internalImage() const +{ + return m_internalImage; +} + template inline T* Scene::Window::windowPixmap() diff --git a/scene.cpp b/scene.cpp --- a/scene.cpp +++ b/scene.cpp @@ -1057,7 +1057,7 @@ bool WindowPixmap::isValid() const { - if (!m_buffer.isNull() || !m_fbo.isNull()) { + if (!m_buffer.isNull() || !m_fbo.isNull() || !m_internalImage.isNull()) { return true; } return m_pixmap != XCB_PIXMAP_NONE; @@ -1108,13 +1108,12 @@ m_buffer->unref(); m_buffer.clear(); } - } else { - // might be an internal window - const auto &fbo = toplevel()->internalFramebufferObject(); - if (!fbo.isNull()) { - m_fbo = fbo; - } } + } else if (toplevel()->internalFramebufferObject()) { + // Might be an internal window. + m_fbo = toplevel()->internalFramebufferObject(); + } else if (!toplevel()->internalImageObject().isNull()) { + m_internalImage = toplevel()->internalImageObject(); } else { if (m_buffer) { QObject::disconnect(m_buffer.data(), &BufferInterface::aboutToBeDestroyed, m_buffer.data(), &BufferInterface::unref); diff --git a/shell_client.h b/shell_client.h --- a/shell_client.h +++ b/shell_client.h @@ -4,6 +4,7 @@ Copyright (C) 2015 Martin Gräßlin Copyright (C) 2018 David Edmundson +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -125,7 +126,6 @@ bool isLockScreen() const override; bool isInputMethod() const override; - virtual QWindow *internalWindow() const; void installPlasmaShellSurface(KWayland::Server::PlasmaShellSurfaceInterface *surface); void installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *decoration); @@ -181,11 +181,6 @@ void doMinimize() override; void updateCaption() override; - virtual bool requestGeometry(const QRect &rect); - virtual void doSetGeometry(const QRect &rect); - void unmap(); - void markAsMapped(); - void setClientSize(const QSize &size) { m_clientSize = size; } @@ -223,6 +218,10 @@ // called on surface commit and processes all m_pendingConfigureRequests up to m_lastAckedConfigureReqest void updatePendingGeometry(); QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSize popupSize) const; + void requestGeometry(const QRect &rect); + void doSetGeometry(const QRect &rect); + void unmap(); + void markAsMapped(); static void deleteClient(ShellClient *c); QSize toWindowGeometry(const QSize &geometry) const; @@ -268,7 +267,6 @@ bool m_fullScreen = false; bool m_transient = false; bool m_hidden = false; - bool m_internal; bool m_hasPopupGrab = false; qreal m_opacity = 1.0; diff --git a/shell_client.cpp b/shell_client.cpp --- a/shell_client.cpp +++ b/shell_client.cpp @@ -4,6 +4,7 @@ Copyright (C) 2015 Martin Gräßlin Copyright (C) 2018 David Edmundson +Copyright (C) 2019 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -73,7 +74,6 @@ , m_shellSurface(surface) , m_xdgShellSurface(nullptr) , m_xdgShellPopup(nullptr) - , m_internal(surface->client() == waylandServer()->internalConnection()) { setSurface(surface->surface()); init(); @@ -85,7 +85,6 @@ , m_shellSurface(nullptr) , m_xdgShellSurface(surface) , m_xdgShellPopup(nullptr) - , m_internal(surface->client() == waylandServer()->internalConnection()) { setSurface(surface->surface()); m_requestGeometryBlockCounter++; @@ -98,7 +97,6 @@ , m_shellSurface(nullptr) , m_xdgShellSurface(nullptr) , m_xdgShellPopup(surface) - , m_internal(surface->client() == waylandServer()->internalConnection()) { setSurface(surface->surface()); m_requestGeometryBlockCounter++; @@ -224,9 +222,7 @@ } else { ready_for_painting = false; } - if (!m_internal) { - doSetGeometry(QRect(QPoint(0, 0), m_clientSize)); - } + doSetGeometry(QRect(QPoint(0, 0), m_clientSize)); if (waylandServer()->inputMethodConnection() == s->client()) { m_windowType = NET::OnScreenDisplay; } @@ -333,7 +329,7 @@ } // set initial desktop - setDesktop(m_internal ? int(NET::OnAllDesktops) : VirtualDesktopManager::self()->current()); + setDesktop(VirtualDesktopManager::self()->current()); // setup shadow integration getShadow(); @@ -1116,9 +1112,7 @@ void ShellClient::createWindowId() { - if (!m_internal) { - m_windowId = waylandServer()->createWindowId(surface()); - } + m_windowId = waylandServer()->createWindowId(surface()); } pid_t ShellClient::pid() const @@ -1136,11 +1130,11 @@ return surface()->client() == waylandServer()->inputMethodConnection(); } -bool ShellClient::requestGeometry(const QRect &rect) +void ShellClient::requestGeometry(const QRect &rect) { if (m_requestGeometryBlockCounter != 0) { m_blockedRequestGeometry = rect; - return false; + return; } QSize size; @@ -1177,7 +1171,6 @@ } m_blockedRequestGeometry = QRect(); - return true; } void ShellClient::updatePendingGeometry() @@ -1249,12 +1242,7 @@ m_plasmaShellSurface = surface; auto updatePosition = [this, surface] { QRect rect = QRect(surface->position(), m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom())); - // Shell surfaces of internal windows are sometimes desync to current value. - // Make sure to not set window geometry of internal windows to invalid values (bug 386304). - // This is a workaround. - if (!m_internal || rect.isValid()) { - doSetGeometry(rect); - } + doSetGeometry(rect); }; auto updateRole = [this, surface] { NET::WindowType type = NET::Unknown; @@ -1779,9 +1767,6 @@ bool ShellClient::shouldExposeToWindowManagement() { - if (m_internal) { - return false; - } if (isLockScreen()) { return false; } @@ -1952,11 +1937,6 @@ return false; } -QWindow *ShellClient::internalWindow() const -{ - return nullptr; -} - bool ShellClient::supportsWindowRules() const { if (m_plasmaShellSurface) { diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -355,7 +355,7 @@ if (window) { windows << static_cast(window)->client()->effectWindow(); } - if (auto t = Workspace::self()->findToplevel(controller)) { + if (Toplevel *t = workspace()->findInternal(controller)) { windows << t->effectWindow(); } static_cast(effects)->highlightWindows(windows); diff --git a/toplevel.h b/toplevel.h --- a/toplevel.h +++ b/toplevel.h @@ -440,9 +440,34 @@ KWayland::Server::SurfaceInterface *surface() const; void setSurface(KWayland::Server::SurfaceInterface *surface); + /** + * TODO: Document this one. + * + * @todo Move this method to the InternalClient class. + **/ virtual void setInternalFramebufferObject(const QSharedPointer &fbo); + + /** + * TODO: Document this one. + * + * @todo Move this method to the InternalClient class. + **/ const QSharedPointer &internalFramebufferObject() const; + /** + * TODO: Document this one. + * + * @todo Move this method to the InternalClient class. + **/ + virtual void setInternalImageObject(const QImage &image); + + /** + * TODO: Document this one. + * + * @todo Move this method to the InternalClient class. + **/ + QImage internalImageObject() const; + /** * @returns Transformation to map from global to window coordinates. * @@ -652,6 +677,7 @@ * An FBO object KWin internal windows might render to. **/ QSharedPointer m_internalFBO; + QImage m_internalImage; // when adding new data members, check also copyToDeleted() qreal m_screenScale = 1.0; }; @@ -918,6 +944,11 @@ return m_internalFBO; } +inline QImage Toplevel::internalImageObject() const +{ + return m_internalImage; +} + inline QPoint Toplevel::clientContentPos() const { return QPoint(0, 0); diff --git a/toplevel.cpp b/toplevel.cpp --- a/toplevel.cpp +++ b/toplevel.cpp @@ -780,6 +780,13 @@ setDepth(32); } +void Toplevel::setInternalImageObject(const QImage &image) +{ + discardWindowPixmap(); + m_internalImage = image; + setDepth(32); +} + QMatrix4x4 Toplevel::inputTransformation() const { QMatrix4x4 m; diff --git a/utils.h b/utils.h --- a/utils.h +++ b/utils.h @@ -63,12 +63,14 @@ class Deleted; class Group; class Options; +class InternalClient; typedef QList< Toplevel* > ToplevelList; typedef QList< Client* > ClientList; typedef QList< const Client* > ConstClientList; typedef QList< Unmanaged* > UnmanagedList; typedef QList< Deleted* > DeletedList; +typedef QList< InternalClient* > InternalClientList; typedef QList< Group* > GroupList; diff --git a/wayland_server.h b/wayland_server.h --- a/wayland_server.h +++ b/wayland_server.h @@ -126,14 +126,10 @@ QList clients() const { return m_clients; } - QList internalClients() const { - return m_internalClients; - } void removeClient(ShellClient *c); ShellClient *findClient(quint32 id) const; ShellClient *findClient(KWayland::Server::SurfaceInterface *surface) const; AbstractClient *findAbstractClient(KWayland::Server::SurfaceInterface *surface) const; - ShellClient *findClient(QWindow *w) const; /** * @returns a transient parent of a surface imported with the foreign protocol, if any @@ -280,7 +276,6 @@ KWayland::Server::XdgForeignInterface *m_XdgForeign = nullptr; KWayland::Server::KeyStateInterface *m_keyState = nullptr; QList m_clients; - QList m_internalClients; QHash m_clientIds; InitalizationFlags m_initFlags; QVector m_plasmaShellSurfaces; diff --git a/wayland_server.cpp b/wayland_server.cpp --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -22,7 +22,6 @@ #include "platform.h" #include "composite.h" #include "idle_inhibition.h" -#include "internal_client.h" #include "screens.h" #include "shell_client.h" #include "workspace.h" @@ -156,12 +155,7 @@ if (surface->client() == m_screenLockerClientConnection) { ScreenLocker::KSldApp::self()->lockScreenShown(); } - ShellClient *client; - if (surface->client() == waylandServer()->internalConnection()) { - client = new InternalClient(surface); - } else { - client = new ShellClient(surface); - } + ShellClient *client = new ShellClient(surface); if (ServerSideDecorationInterface *deco = ServerSideDecorationInterface::get(surface->surface())) { client->installServerSideDecoration(deco); } @@ -180,11 +174,7 @@ if (auto palette = m_paletteManager->paletteForSurface(surface->surface())) { client->installPalette(palette); } - if (client->isInternal()) { - m_internalClients << client; - } else { - m_clients << client; - } + m_clients << client; if (client->readyForPainting()) { emit shellClientAdded(client); } else { @@ -601,7 +591,6 @@ void WaylandServer::removeClient(ShellClient *c) { m_clients.removeAll(c); - m_internalClients.removeAll(c); emit shellClientRemoved(c); } @@ -650,9 +639,6 @@ if (ShellClient *c = findClientInList(m_clients, id)) { return c; } - if (ShellClient *c = findClientInList(m_internalClients, id)) { - return c; - } return nullptr; } @@ -664,33 +650,14 @@ if (ShellClient *c = findClientInList(m_clients, surface)) { return c; } - if (ShellClient *c = findClientInList(m_internalClients, surface)) { - return c; - } return nullptr; } AbstractClient *WaylandServer::findAbstractClient(SurfaceInterface *surface) const { return findClient(surface); } -ShellClient *WaylandServer::findClient(QWindow *w) const -{ - if (!w) { - return nullptr; - } - auto it = std::find_if(m_internalClients.constBegin(), m_internalClients.constEnd(), - [w] (const ShellClient *c) { - return c->internalWindow() == w; - } - ); - if (it != m_internalClients.constEnd()) { - return *it; - } - return nullptr; -} - quint32 WaylandServer::createWindowId(SurfaceInterface *surface) { auto it = m_clientIds.constFind(surface->client()); diff --git a/workspace.h b/workspace.h --- a/workspace.h +++ b/workspace.h @@ -129,11 +129,6 @@ Unmanaged *findUnmanaged(xcb_window_t w) const; void forEachUnmanaged(std::function func); Toplevel *findToplevel(std::function func) const; - /** - * Finds the Toplevel for the KWin internal window @p w. - * On Wayland this is normally a ShellClient. For X11 an Unmanaged. - **/ - Toplevel *findToplevel(QWindow *w) const; /** * @brief Finds a Toplevel for the internal window @p w. * @@ -240,6 +235,13 @@ return m_allClients; } + /** + * @returns List of all internal clients currently managed by Workspace + **/ + const InternalClientList &internalClients() const { + return m_internalClients; + } + void stackScreenEdgesUnderOverrideRedirect(); public: @@ -503,6 +505,16 @@ **/ void stackingOrderChanged(); + /** + * This signal is emitted whenever an internal client is created. + **/ + void internalClientAdded(KWin::InternalClient *client); + + /** + * This signal is emitted whenever an internal client gets removed. + **/ + void internalClientRemoved(KWin::InternalClient *client); + private: void init(); void initWithX11(); @@ -534,6 +546,9 @@ Unmanaged* createUnmanaged(xcb_window_t w); void addUnmanaged(Unmanaged* c); + void addInternalClient(InternalClient *client); + void removeInternalClient(InternalClient *client); + //--------------------------------------------------------------------- void closeActivePopup(); @@ -569,6 +584,7 @@ ClientList desktops; UnmanagedList unmanaged; DeletedList deleted; + InternalClientList m_internalClients; ToplevelList unconstrained_stacking_order; // Topmost last ToplevelList stacking_order; // Topmost last @@ -636,6 +652,8 @@ QList m_genericEventFilters; QScopedPointer m_movingClientFilter; + friend class InternalClient; + private: friend bool performTransiencyCheck(); friend Workspace *workspace(); diff --git a/workspace.cpp b/workspace.cpp --- a/workspace.cpp +++ b/workspace.cpp @@ -37,6 +37,7 @@ #include "focuschain.h" #include "group.h" #include "input.h" +#include "internal_client.h" #include "logind.h" #include "moving_client_x11_filter.h" #include "killwindow.h" @@ -1649,7 +1650,7 @@ return ret; } if (waylandServer()) { - if (AbstractClient *ret = Toplevel::findInList(waylandServer()->internalClients(), func)) { + if (AbstractClient *ret = Toplevel::findInList(workspace()->internalClients(), func)) { return ret; } } @@ -1705,19 +1706,6 @@ return nullptr; } -Toplevel *Workspace::findToplevel(QWindow *w) const -{ - if (!w) { - return nullptr; - } - if (waylandServer()) { - if (auto c = waylandServer()->findClient(w)) { - return c; - } - } - return findUnmanaged(w->winId()); -} - bool Workspace::hasClient(const AbstractClient *c) { if (auto cc = dynamic_cast(c)) { @@ -1743,9 +1731,13 @@ } if (kwinApp()->operationMode() == Application::OperationModeX11) { return findUnmanaged(w->winId()); - } else { - return waylandServer()->findClient(w); } + for (InternalClient *client : m_internalClients) { + if (client->internalWindow() == w) { + return client; + } + } + return nullptr; } bool Workspace::compositing() const @@ -1775,5 +1767,31 @@ ); } +void Workspace::addInternalClient(InternalClient *client) +{ + m_internalClients.append(client); + + setupClientConnections(client); + client->updateDecoration(false); + client->updateLayer(); + + markXStackingOrderAsDirty(); + updateStackingOrder(true); + updateClientArea(); + + emit internalClientAdded(client); +} + +void Workspace::removeInternalClient(InternalClient *client) +{ + m_internalClients.removeOne(client); + + markXStackingOrderAsDirty(); + updateStackingOrder(true); + updateClientArea(); + + emit internalClientRemoved(client); +} + } // namespace