diff --git a/abstract_client.h b/abstract_client.h --- a/abstract_client.h +++ b/abstract_client.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 @@ -834,6 +835,14 @@ */ virtual bool supportsWindowRules() const; + /** + * Returns the extents of the server-side decoration. + * + * Note that the returned margins object will have all margins set to 0 if + * the client doesn't have a server-side decoration. + */ + 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 @@ -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 @@ -1992,4 +1993,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)); @@ -497,8 +500,7 @@ w->hide(); w.reset(); - QVERIFY(rowsRemovedSpy.wait()); - QCOMPARE(rowsRemovedSpy.count(), 1); + QTRY_COMPARE(rowsRemovedSpy.count(), 1); QCOMPARE(rowsRemovedSpy.first().first().value(), internalTopLevelIndex); } @@ -511,12 +513,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,9 @@ void DecorationInputTest::initTestCase() { - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); @@ -910,12 +912,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" @@ -64,6 +65,7 @@ qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); @@ -248,11 +250,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 +264,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/globalshortcuts_test.cpp b/autotests/integration/globalshortcuts_test.cpp --- a/autotests/integration/globalshortcuts_test.cpp +++ b/autotests/integration/globalshortcuts_test.cpp @@ -21,6 +21,7 @@ #include "client.h" #include "cursor.h" #include "input.h" +#include "internal_client.h" #include "platform.h" #include "screens.h" #include "shell_client.h" @@ -63,8 +64,9 @@ void GlobalShortcutsTest::initTestCase() { - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); @@ -346,11 +348,11 @@ QVERIFY(client->isActive()); QCOMPARE(client->shortcut(), QKeySequence()); - QSignalSpy shortcutDialogAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QSignalSpy shortcutDialogAddedSpy(workspace(), &Workspace::internalClientAdded); QVERIFY(shortcutDialogAddedSpy.isValid()); workspace()->slotSetupWindowShortcut(); QTRY_COMPARE(shortcutDialogAddedSpy.count(), 1); - auto dialog = shortcutDialogAddedSpy.first().first().value(); + auto dialog = shortcutDialogAddedSpy.first().first().value(); QVERIFY(dialog); QVERIFY(dialog->isInternal()); auto sequenceEdit = workspace()->shortcutDialog()->findChild(); 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,9 @@ void InternalWindowTest::initTestCase() { - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); QVERIFY(workspaceCreatedSpy.isValid()); kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); @@ -211,26 +212,27 @@ void InternalWindowTest::cleanup() { Test::destroyWaylandConnection(); + + QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); } 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); 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)); @@ -262,21 +264,11 @@ // inside the mask we should still get an enter kwinApp()->platform()->pointerMotion(QPoint(25, 27), timestamp++); QTRY_COMPARE(enterSpy.count(), 2); - - // hide the window, which should be removed from the stacking order - win.hide(); - QTRY_VERIFY(!c->isShown(false)); - QVERIFY(!workspace()->xStackingOrder().contains(c)); - - // show again - win.show(); - QTRY_VERIFY(c->isShown(false)); - QVERIFY(workspace()->xStackingOrder().contains(c)); } 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 +291,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 +319,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 +329,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 +348,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 +359,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 +404,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 +414,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 +441,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 +505,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 +568,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 +592,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 +634,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 +668,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,18 +687,16 @@ 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(); - QCOMPARE(internalClient->surface()->scale(), 2); - - QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); + auto internalClient = clientAddedSpy.first().first().value(); + QCOMPARE(internalClient->bufferScale(), 2); } void InternalWindowTest::testWindowType_data() @@ -732,15 +722,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 +757,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 +777,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" @@ -357,18 +358,17 @@ c->setupCompositing(); c->getShadow(); } + for (InternalClient *client : workspace()->internalClients()) { + client->setupCompositing(); + client->getShadow(); + } if (auto *server = waylandServer()) { const auto clients = server->clients(); for (ShellClient *c : clients) { c->setupCompositing(); c->getShadow(); } - const auto internalClients = server->internalClients(); - for (ShellClient *c : internalClients) { - c->setupCompositing(); - c->getShadow(); - } } m_state = State::On; @@ -415,6 +415,9 @@ for (Unmanaged *c : Workspace::self()->unmanagedList()) { m_scene->removeToplevel(c); } + for (InternalClient *client : workspace()->internalClients()) { + m_scene->removeToplevel(client); + } for (Client *c : Workspace::self()->clientList()) { c->finishCompositing(); } @@ -424,6 +427,9 @@ for (Unmanaged *c : Workspace::self()->unmanagedList()) { c->finishCompositing(); } + for (InternalClient *client : workspace()->internalClients()) { + client->finishCompositing(); + } if (auto *con = kwinApp()->x11Connection()) { xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); @@ -437,15 +443,9 @@ for (ShellClient *c : waylandServer()->clients()) { m_scene->removeToplevel(c); } - for (ShellClient *c : waylandServer()->internalClients()) { - m_scene->removeToplevel(c); - } for (ShellClient *c : waylandServer()->clients()) { c->finishCompositing(); } - for (ShellClient *c : waylandServer()->internalClients()) { - c->finishCompositing(); - } } delete m_scene; @@ -754,13 +754,13 @@ if (std::any_of(clients.begin(), clients.end(), test)) { return true; } - const auto &internalClients = server->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 *client) { + return client->isShown(true) && !client->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" @@ -762,7 +763,7 @@ static const int s_x11ClientId = 1; static const int s_x11UnmanagedId = 2; static const int s_waylandClientId = 3; -static const int s_waylandInternalId = 4; +static const int s_workspaceInternalId = 4; static const quint32 s_propertyBitMask = 0xFFFF0000; static const quint32 s_clientBitMask = 0x0000FFFF; static const quint32 s_idDistance = 10000; @@ -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,19 @@ remove(s_x11UnmanagedId -1, m_unmanageds, u); } ); + for (InternalClient *client : workspace()->internalClients()) { + m_internalClients.append(client); + } + connect(workspace(), &Workspace::internalClientAdded, this, + [this](InternalClient *client) { + add(s_workspaceInternalId -1, m_internalClients, client); + } + ); + connect(workspace(), &Workspace::internalClientRemoved, this, + [this](InternalClient *client) { + remove(s_workspaceInternalId -1, m_internalClients, client); + } + ); } DebugConsoleModel::~DebugConsoleModel() = default; @@ -890,7 +895,7 @@ return m_unmanageds.count(); case s_waylandClientId: return m_shellClients.count(); - case s_waylandInternalId: + case s_workspaceInternalId: return m_internalClients.count(); default: break; @@ -907,7 +912,7 @@ return propertyCount(parent, &DebugConsoleModel::unmanaged); } else if (parent.internalId() < s_idDistance * (s_waylandClientId + 1)) { return propertyCount(parent, &DebugConsoleModel::shellClient); - } else if (parent.internalId() < s_idDistance * (s_waylandInternalId + 1)) { + } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) { return propertyCount(parent, &DebugConsoleModel::internalClient); } @@ -959,8 +964,8 @@ return indexForClient(row, column, m_unmanageds, s_x11UnmanagedId); case s_waylandClientId: return indexForClient(row, column, m_shellClients, s_waylandClientId); - case s_waylandInternalId: - return indexForClient(row, column, m_internalClients, s_waylandInternalId); + case s_workspaceInternalId: + return indexForClient(row, column, m_internalClients, s_workspaceInternalId); default: break; } @@ -972,16 +977,16 @@ return indexForProperty(row, column, parent, &DebugConsoleModel::unmanaged); } else if (parent.internalId() < s_idDistance * (s_waylandClientId + 1)) { return indexForProperty(row, column, parent, &DebugConsoleModel::shellClient); - } else if (parent.internalId() < s_idDistance * (s_waylandInternalId + 1)) { + } else if (parent.internalId() < s_idDistance * (s_workspaceInternalId + 1)) { return indexForProperty(row, column, parent, &DebugConsoleModel::internalClient); } return QModelIndex(); } QModelIndex DebugConsoleModel::parent(const QModelIndex &child) const { - if (child.internalId() <= s_waylandInternalId) { + if (child.internalId() <= s_workspaceInternalId) { return QModelIndex(); } if (child.internalId() & s_propertyBitMask) { @@ -993,8 +998,8 @@ return createIndex(parentId - (s_idDistance * s_x11UnmanagedId), 0, parentId); } else if (parentId < s_idDistance * (s_waylandClientId + 1)) { return createIndex(parentId - (s_idDistance * s_waylandClientId), 0, parentId); - } else if (parentId < s_idDistance * (s_waylandInternalId + 1)) { - return createIndex(parentId - (s_idDistance * s_waylandInternalId), 0, parentId); + } else if (parentId < s_idDistance * (s_workspaceInternalId + 1)) { + return createIndex(parentId - (s_idDistance * s_workspaceInternalId), 0, parentId); } return QModelIndex(); } @@ -1004,8 +1009,8 @@ return createIndex(s_x11UnmanagedId -1, 0, s_x11UnmanagedId); } else if (child.internalId() < s_idDistance * (s_waylandClientId + 1)) { return createIndex(s_waylandClientId -1, 0, s_waylandClientId); - } else if (child.internalId() < s_idDistance * (s_waylandInternalId + 1)) { - return createIndex(s_waylandInternalId -1, 0, s_waylandInternalId); + } else if (child.internalId() < s_idDistance * (s_workspaceInternalId + 1)) { + return createIndex(s_workspaceInternalId -1, 0, s_workspaceInternalId); } return QModelIndex(); } @@ -1098,7 +1103,7 @@ return i18n("X11 Unmanaged Windows"); case s_waylandClientId: return i18n("Wayland Windows"); - case s_waylandInternalId: + case s_workspaceInternalId: return i18n("Internal Windows"); default: return QVariant(); @@ -1110,7 +1115,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); @@ -1136,7 +1141,7 @@ } case s_waylandClientId: return clientData(index, role, m_shellClients); - case s_waylandInternalId: + case s_workspaceInternalId: return clientData(index, role, m_internalClients); default: break; @@ -1161,9 +1166,9 @@ 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); + return clientForIndex(index, m_internalClients, s_workspaceInternalId); } Client *DebugConsoleModel::x11Client(const QModelIndex &index) const @@ -1207,9 +1212,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); @@ -1255,12 +1257,10 @@ } 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()->unmanagedList().count(); } QModelIndex SurfaceTreeModel::index(int row, int column, const QModelIndex &parent) const @@ -1297,12 +1297,6 @@ 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()); - } - } // not found return QModelIndex(); } @@ -1359,14 +1353,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/deleted.h b/deleted.h --- a/deleted.h +++ b/deleted.h @@ -44,6 +44,7 @@ void refWindow(); void unrefWindow(); void discard(); + qreal bufferScale() const override; int desktop() const override; QStringList activities() const override; QVector desktops() const override; @@ -233,6 +234,7 @@ DeletedList m_transients; bool m_wasPopupWindow; bool m_wasOutline; + qreal m_bufferScale = 1; }; inline void Deleted::refWindow() diff --git a/deleted.cpp b/deleted.cpp --- a/deleted.cpp +++ b/deleted.cpp @@ -94,6 +94,7 @@ { Q_ASSERT(dynamic_cast< Deleted* >(c) == nullptr); Toplevel::copyToDeleted(c); + m_bufferScale = c->bufferScale(); desk = c->desktop(); m_desktops = c->desktops(); activityList = c->activities(); @@ -146,7 +147,7 @@ } m_wasWaylandClient = qobject_cast(c) != nullptr; - m_wasX11Client = !m_wasWaylandClient; + m_wasX11Client = qobject_cast(c) != nullptr; m_wasPopupWindow = c->isPopupWindow(); m_wasOutline = c->isOutline(); } @@ -162,6 +163,11 @@ deleteLater(); } +qreal Deleted::bufferScale() const +{ + return m_bufferScale; +} + int Deleted::desktop() const { return desk; 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" @@ -178,6 +179,12 @@ connect(u, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotUnmanagedShown); } ); + connect(ws, &Workspace::internalClientAdded, this, + [this](InternalClient *client) { + setupAbstractClientConnections(client); + emit windowAdded(client->effectWindow()); + } + ); connect(ws, &Workspace::clientActivated, this, [this](KWin::AbstractClient *c) { emit windowActivated(c ? c->effectWindow() : nullptr); @@ -245,6 +252,9 @@ for (Unmanaged *u : ws->unmanagedList()) { setupUnmanagedConnections(u); } + for (InternalClient *client : ws->internalClients()) { + setupAbstractClientConnections(client); + } if (auto w = waylandServer()) { connect(w, &WaylandServer::shellClientAdded, this, [this](ShellClient *c) { @@ -1087,13 +1097,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; } @@ -1716,7 +1721,7 @@ managed = toplevel->isClient(); waylandClient = qobject_cast(toplevel) != nullptr; - x11Client = !waylandClient; + x11Client = qobject_cast(toplevel) != nullptr; } EffectWindowImpl::~EffectWindowImpl() @@ -1969,7 +1974,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 @@ -268,10 +269,6 @@ for (auto c : clients) { updateStrutsForWaylandClient(c); } - const auto internalClients = waylandServer()->internalClients(); - for (auto c : internalClients) { - updateStrutsForWaylandClient(c); - } } #if 0 for (int i = 1; diff --git a/idle_inhibition.cpp b/idle_inhibition.cpp --- a/idle_inhibition.cpp +++ b/idle_inhibition.cpp @@ -94,6 +94,10 @@ void IdleInhibition::update(AbstractClient *client) { + if (client->isInternal()) { + return; + } + // TODO: Don't honor the idle inhibitor object if the shell client is not // on the current activity (currently, activities are not supported). const bool visible = client->isShown(true) && client->isOnCurrentDesktop(); 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 = qobject_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 = qobject_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 QList &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 QList &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,40 @@ *********************************************************************/ #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; + QStringList activities() const override; + void blockActivityUpdates(bool b = true) override; + qreal bufferScale() const override; + QString captionNormal() const override; + QString captionSuffix() const override; + QPoint clientContentPos() const override; + QSize clientSize() const override; + void debug(QDebug &stream) const override; + QRect transparentRect() const override; NET::WindowType windowType(bool direct = false, int supported_types = 0) const override; + double opacity() const override; + void setOpacity(double opacity) override; void killWindow() override; bool isPopupWindow() const override; - void setInternalFramebufferObject(const QSharedPointer &fbo) 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 +67,64 @@ 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 &rect) 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(); + void present(const QSharedPointer fbo); + void present(const QImage &image, const QRegion &damage); + 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 destroyDecoration() override; void doMove(int x, int y) override; void doResizeSync() override; - bool requestGeometry(const QRect &rect) override; - void doSetGeometry(const QRect &rect) override; + void updateCaption() override; private: - void findInternalWindow(); - void updateInternalWindowGeometry(); + QRect mapFromClient(const QRect &rect) const; + QRect mapToClient(const QRect &rect) const; + void createDecoration(const QRect &rect); + void requestGeometry(const QRect &rect); + void commitGeometry(const QRect &rect); + void setCaption(const QString &caption); + void markAsMapped(); void syncGeometryToInternalWindow(); + void updateInternalWindowGeometry(); + QWindow *m_internalWindow = nullptr; + QRect m_maximizeRestoreGeometry; + 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_userNoBorder = false; + + 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,67 @@ along with this program. If not, see . *********************************************************************/ #include "internal_client.h" +#include "decorations/decorationbridge.h" +#include "deleted.h" #include "workspace.h" -#include -#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_clientSize(window->size()) + , m_windowId(window->winId()) + , m_internalWindowFlags(window->flags()) { - findInternalWindow(); - updateInternalWindowGeometry(); - updateDecoration(true); -} + // Don't render the client until it provides a buffer. + ready_for_painting = false; -InternalClient::InternalClient(KWayland::Server::XdgShellSurfaceInterface *surface) - : ShellClient(surface) -{ -} + 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::windowTitleChanged, this, &InternalClient::setCaption); + connect(m_internalWindow, &QWindow::opacityChanged, this, &InternalClient::setOpacity); + connect(m_internalWindow, &QWindow::destroyed, this, &InternalClient::destroyClient); -InternalClient::InternalClient(KWayland::Server::XdgShellPopupInterface *surface) - : ShellClient(surface) -{ -} + connect(this, &InternalClient::opacityChanged, this, &InternalClient::addRepaintFull); -InternalClient::~InternalClient() = default; + const QVariant windowType = m_internalWindow->property("kwin_windowType"); + if (!windowType.isNull()) { + m_windowType = windowType.value(); + } -void InternalClient::findInternalWindow() -{ - 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()); + setCaption(m_internalWindow->title()); + setIcon(QIcon::fromTheme(QStringLiteral("kwin"))); + setOnAllDesktops(true); + setOpacity(m_internalWindow->opacity()); + setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); - // skip close animation support - setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); - m_internalWindow->installEventFilter(this); - return; - } + setupCompositing(); + updateColorScheme(); - qCWarning(KWIN_CORE, "Couldn't find an internal window for surface with id %x", surface()->id()); + blockGeometryUpdates(true); + commitGeometry(m_internalWindow->geometry()); + updateDecoration(true); + setGeometry(mapFromClient(m_internalWindow->geometry())); + setGeometryRestore(geometry()); + blockGeometryUpdates(false); + + m_internalWindow->installEventFilter(this); +} + +InternalClient::~InternalClient() +{ } bool InternalClient::eventFilter(QObject *watched, QEvent *event) @@ -101,38 +97,96 @@ return false; } +QStringList InternalClient::activities() const +{ + return QStringList(); +} + +void InternalClient::blockActivityUpdates(bool b) +{ + Q_UNUSED(b) + + // Internal clients do not support activities. +} + +qreal InternalClient::bufferScale() const +{ + if (m_internalWindow) { + return m_internalWindow->devicePixelRatio(); + } + return 1; +} + +QString InternalClient::captionNormal() const +{ + return m_captionNormal; +} + +QString InternalClient::captionSuffix() const +{ + return m_captionSuffix; +} + +QPoint InternalClient::clientContentPos() const +{ + return -1 * clientPos(); +} + +QSize InternalClient::clientSize() const +{ + return m_clientSize; +} + +void InternalClient::debug(QDebug &stream) const +{ + stream.nospace() << "\'InternalClient:" << m_internalWindow << "\'"; +} + +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; } +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) +QByteArray InternalClient::windowRole() const { - if (fbo.isNull()) { - unmap(); - return; - } - - setClientSize(fbo->size() / surface()->scale()); - markAsMapped(); - doSetGeometry(QRect(geom.topLeft(), clientSize())); - Toplevel::setInternalFramebufferObject(fbo); - Toplevel::addDamage(QRegion(0, 0, width(), height())); + return QByteArray(); } void InternalClient::closeWindow() @@ -147,6 +201,16 @@ return true; } +bool InternalClient::isFullScreenable() const +{ + return false; +} + +bool InternalClient::isFullScreen() const +{ + return false; +} + bool InternalClient::isMaximizable() const { return false; @@ -174,7 +238,7 @@ bool InternalClient::noBorder() const { - return m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); + return m_userNoBorder || m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); } bool InternalClient::userCanSetNoBorder() const @@ -187,11 +251,6 @@ return false; } -bool InternalClient::acceptsFocus() const -{ - return false; -} - bool InternalClient::isInternal() const { return true; @@ -226,111 +285,401 @@ return m_windowId; } -void InternalClient::updateInternalWindowGeometry() +MaximizeMode InternalClient::maximizeMode() const +{ + return MaximizeRestore; +} + +QRect InternalClient::geometryRestore() const +{ + return m_maximizeRestoreGeometry; +} + +bool InternalClient::isShown(bool shaded_is_shown) const { + Q_UNUSED(shaded_is_shown) + + return readyForPainting(); +} + +bool InternalClient::isHiddenInternal() const +{ + return false; +} + +void InternalClient::hideClient(bool hide) +{ + Q_UNUSED(hide) +} + +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(); + } + setGeometry(QRect(x(), y(), w, h)); } -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; + const QRect rect(x, y, w, h); + + if (areGeometryUpdatesBlocked()) { + geom = rect; + 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(); + } + + if (geom == rect) { + return; + } + + const QRect newClientGeometry = mapToClient(rect); + + if (m_clientSize == newClientGeometry.size()) { + commitGeometry(rect); + } else { + requestGeometry(rect); } - return true; } -void InternalClient::doSetGeometry(const QRect &rect) +void InternalClient::setGeometryRestore(const QRect &rect) { - if (geom == rect && pendingGeometryUpdate() == PendingGeometryNone) { + m_maximizeRestoreGeometry = rect; +} + +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() +{ +} + +bool InternalClient::userCanSetFullScreen() const +{ + return false; +} + +void InternalClient::setFullScreen(bool set, bool user) +{ + Q_UNUSED(set) + Q_UNUSED(user) +} + +void InternalClient::setNoBorder(bool set) +{ + if (!userCanSetNoBorder()) { return; } - if (!isUnmapped()) { - addWorkspaceRepaint(visibleRect()); + if (m_userNoBorder == set) { + return; } - geom = rect; + m_userNoBorder = set; + updateDecoration(true); +} - if (isUnmapped() && geometryRestore().isEmpty() && !geom.isEmpty()) { - // use first valid geometry as restore geometry - setGeometryRestore(geom); +void InternalClient::updateDecoration(bool check_workspace_pos, bool force) +{ + if (!force && isDecorated() == !noBorder()) { + return; } - if (!isUnmapped()) { - addWorkspaceRepaint(visibleRect()); + const QRect oldFrameGeometry = geometry(); + const QRect oldClientGeometry = oldFrameGeometry - frameMargins(); + + GeometryUpdatesBlocker blocker(this); + + if (force) { + destroyDecoration(); } - syncGeometryToInternalWindow(); - if (hasStrut()) { - workspace()->updateClientArea(); + + if (!noBorder()) { + createDecoration(oldClientGeometry); + } else { + destroyDecoration(); } - const auto old = geometryBeforeUpdateBlocking(); - updateGeometryBeforeUpdateBlocking(); - emit geometryShapeChanged(this, old); - if (isResize()) { - performMoveResize(); + getShadow(); + + if (check_workspace_pos) { + checkWorkspacePosition(oldFrameGeometry, -2, oldClientGeometry); + } +} + +void InternalClient::updateColorScheme() +{ + AbstractClient::updateColorScheme(QString()); +} + +void InternalClient::showOnScreenEdge() +{ +} + +void InternalClient::destroyClient() +{ + if (isMoveResize()) { + leaveMoveResize(); } + + Deleted *deleted = Deleted::create(this); + emit windowClosed(this, deleted); + + destroyDecoration(); + + workspace()->removeInternalClient(this); + + deleted->unrefWindow(); + m_internalWindow = nullptr; + + delete this; +} + +void InternalClient::present(const QSharedPointer fbo) +{ + Q_ASSERT(m_internalImage.isNull()); + + const QSize bufferSize = fbo->size() / bufferScale(); + + commitGeometry(QRect(pos(), sizeForClientSize(bufferSize))); + markAsMapped(); + + if (m_internalFBO != fbo) { + discardWindowPixmap(); + m_internalFBO = fbo; + } + + setDepth(32); + addDamageFull(); + addRepaintFull(); +} + +void InternalClient::present(const QImage &image, const QRegion &damage) +{ + Q_ASSERT(m_internalFBO.isNull()); + + const QSize bufferSize = image.size() / bufferScale(); + + commitGeometry(QRect(pos(), sizeForClientSize(bufferSize))); + markAsMapped(); + + if (m_internalImage.size() != image.size()) { + discardWindowPixmap(); + } + + m_internalImage = image; + + setDepth(32); + addDamage(damage); + addRepaint(damage.translated(borderLeft(), borderTop())); +} + +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::changeMaximize(bool horizontal, bool vertical, bool adjust) +{ + Q_UNUSED(horizontal) + Q_UNUSED(vertical) + Q_UNUSED(adjust) + + // Internal clients are not maximizable. +} + +void InternalClient::destroyDecoration() +{ + if (!isDecorated()) { + return; + } + + const QRect clientGeometry = mapToClient(geometry()); + AbstractClient::destroyDecoration(); + setGeometry(clientGeometry); } void InternalClient::doMove(int x, int y) { Q_UNUSED(x) Q_UNUSED(y) + syncGeometryToInternalWindow(); } -void InternalClient::syncGeometryToInternalWindow() +void InternalClient::doResizeSync() { - if (!m_internalWindow) { - return; + requestGeometry(moveResizeGeometry()); +} + +void InternalClient::updateCaption() +{ + const QString oldSuffix = m_captionSuffix; + const auto shortcut = shortcutCaptionSuffix(); + m_captionSuffix = shortcut; + if ((!isSpecialWindow() || isToolbar()) && findClientWithSameCaption()) { + int i = 2; + do { + m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>'); + i++; + } while (findClientWithSameCaption()); } - 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)); + if (m_captionSuffix != oldSuffix) { + emit captionChanged(); } } -void InternalClient::resizeWithChecks(int w, int h, ForceGeometry_t force) +QRect InternalClient::mapFromClient(const QRect &rect) const { - Q_UNUSED(force) - if (!m_internalWindow) { - return; + return rect + frameMargins(); +} + +QRect InternalClient::mapToClient(const QRect &rect) const +{ + return rect - frameMargins(); +} + +void InternalClient::createDecoration(const QRect &rect) +{ + KDecoration2::Decoration *decoration = Decoration::DecorationBridge::self()->createDecoration(this); + if (decoration) { + QMetaObject::invokeMethod(decoration, "update", Qt::QueuedConnection); + connect(decoration, &KDecoration2::Decoration::shadowChanged, this, &Toplevel::getShadow); + connect(decoration, &KDecoration2::Decoration::bordersChanged, this, + [this]() { + GeometryUpdatesBlocker blocker(this); + const QRect oldGeometry = geometry(); + if (!isShade()) { + checkWorkspacePosition(oldGeometry); + } + emit geometryShapeChanged(this, oldGeometry); + } + ); } - QRect area = workspace()->clientArea(WorkArea, this); - // don't allow growing larger than workarea - if (w > area.width()) { - w = area.width(); + + const QRect oldFrameGeometry = geometry(); + + setDecoration(decoration); + setGeometry(mapFromClient(rect)); + + emit geometryShapeChanged(this, oldFrameGeometry); +} + +void InternalClient::requestGeometry(const QRect &rect) +{ + if (m_internalWindow) { + m_internalWindow->setGeometry(mapToClient(rect)); } - if (h > area.height()) { - h = area.height(); +} + +void InternalClient::commitGeometry(const QRect &rect) +{ + if (geom == rect && pendingGeometryUpdate() == PendingGeometryNone) { + return; + } + + geom = rect; + + m_clientSize = mapToClient(geometry()).size(); + + addWorkspaceRepaint(visibleRect()); + syncGeometryToInternalWindow(); + + const QRect oldGeometry = geometryBeforeUpdateBlocking(); + updateGeometryBeforeUpdateBlocking(); + emit geometryShapeChanged(this, oldGeometry); + + if (isResize()) { + performMoveResize(); } - m_internalWindow->setGeometry(QRect(pos() + QPoint(borderLeft(), borderTop()), QSize(w, h) - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); } -void InternalClient::doResizeSync() +void InternalClient::setCaption(const QString &caption) { - if (!m_internalWindow) { + if (m_captionNormal == caption) { return; } - const auto rect = moveResizeGeometry(); - m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); + + m_captionNormal = caption; + + const QString oldCaptionSuffix = m_captionSuffix; + updateCaption(); + + if (m_captionSuffix == oldCaptionSuffix) { + emit captionChanged(); + } } -QWindow *InternalClient::internalWindow() const +void InternalClient::markAsMapped() { - return m_internalWindow; + if (!ready_for_painting) { + setReadyForPainting(); + workspace()->addInternalClient(this); + } } -bool InternalClient::supportsWindowRules() const +void InternalClient::syncGeometryToInternalWindow() { - return false; + if (m_internalWindow->geometry() == mapToClient(geometry())) { + return; + } + + QTimer::singleShot(0, this, [this] { requestGeometry(geometry()); }); +} + +void InternalClient::updateInternalWindowGeometry() +{ + if (isMoveResize()) { + return; + } + + commitGeometry(mapFromClient(m_internalWindow->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 @@ -761,14 +762,13 @@ } } } - if (waylandServer()) { - const auto clients = waylandServer()->internalClients(); - for (auto c: clients) { - if (c->isShown(false)) { - x_stacking << c; - } + + for (InternalClient *client : workspace()->internalClients()) { + 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 @@ -111,8 +111,10 @@ bool loadShmTexture(const QPointer &buffer); bool loadEglTexture(const QPointer &buffer); bool loadDmabufTexture(const QPointer< KWayland::Server::BufferInterface > &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 @@ -344,11 +344,16 @@ bool AbstractEglTexture::loadTexture(WindowPixmap *pixmap) { + // FIXME: Refactor this method. + const auto &buffer = pixmap->buffer(); if (buffer.isNull()) { if (updateFromFBO(pixmap->fbo())) { return true; } + if (loadInternalImageObject(pixmap)) { + return true; + } return false; } // try Wayland loading @@ -365,13 +370,14 @@ void AbstractEglTexture::updateTexture(WindowPixmap *pixmap) { + // FIXME: Refactor this method. + 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; @@ -554,6 +560,58 @@ return true; } +bool AbstractEglTexture::loadInternalImageObject(WindowPixmap *pixmap) +{ + // FIXME: Share some code with loadShmTexture(). + + 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; @@ -596,5 +654,55 @@ return true; } +bool AbstractEglTexture::updateFromInternalImageObject(WindowPixmap *pixmap) +{ + // FIXME: Share some code with the shm fallback in updateTexture(). + + 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 @@ -31,7 +30,6 @@ ${QT5PLATFORMSUPPORT_LIBS} ${FREETYPE_LIBRARIES} # Must be after QT5PLATFORMSUPPORT_LIBS Fontconfig::Fontconfig - KF5::WaylandClient kwin ) 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,24 @@ #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; + QImage m_frontBuffer; }; } 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,19 @@ 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" 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 +43,60 @@ 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); + + m_frontBuffer = QImage(size * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); + m_frontBuffer.setDevicePixelRatio(devicePixelRatio); } -void BackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +static void blitImage(const QImage &source, QImage &target, const QRect &rect) { - Q_UNUSED(region) - Q_UNUSED(offset) + Q_ASSERT(source.format() == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(target.format() == QImage::Format_ARGB32_Premultiplied); - auto w = static_cast(window->handle()); - auto s = w->surface(); - if (!s) { - return; + const int devicePixelRatio = target.devicePixelRatio(); + + const int x = rect.x() * devicePixelRatio; + const int y = rect.y() * devicePixelRatio; + const int width = rect.width() * devicePixelRatio; + const int height = rect.height() * devicePixelRatio; + + for (int i = y; i < y + height; ++i) { + const uint32_t *in = reinterpret_cast(source.scanLine(i)); + uint32_t *out = reinterpret_cast(target.scanLine(i)); + std::copy(in + x, in + x + width, out + x); } - 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&) +static void blitImage(const QImage &source, QImage &target, const QRegion ®ion) { - 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); + for (const QRect &rect : region) { + blitImage(source, target, rect); } } -int BackingStore::scale() const +void BackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { - return static_cast(window()->handle())->scale(); + Q_UNUSED(offset) + + Window *platformWindow = static_cast(window->handle()); + InternalClient *client = platformWindow->client(); + if (!client) { + return; + } + + blitImage(m_backBuffer, m_frontBuffer, region); + + client->present(m_frontBuffer, region); } } diff --git a/plugins/qpa/integration.h b/plugins/qpa/integration.h --- a/plugins/qpa/integration.h +++ b/plugins/qpa/integration.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 @@ -27,15 +28,6 @@ #include #include -namespace KWayland -{ -namespace Client -{ -class Compositor; -class Shell; -} -} - namespace KWin { namespace QPA @@ -58,24 +50,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 @@ -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 @@ -19,23 +20,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 +51,6 @@ : QObject() , QPlatformIntegration() , m_fontDb(new QGenericUnixFontDatabase()) - , m_nativeInterface(new NativeInterface(this)) , m_inputContext() { } @@ -139,25 +130,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 +161,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 +208,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->present(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/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -1534,15 +1534,35 @@ { } +static bool needsPixmapUpdate(const OpenGLWindowPixmap *pixmap) +{ + // That's a regular Wayland client. + if (pixmap->surface()) { + return !pixmap->surface()->trackedDamage().isEmpty(); + } + + // That's an internal client with a raster buffer attached. + if (!pixmap->internalImage().isNull()) { + return !pixmap->toplevel()->damage().isEmpty(); + } + + // That's an internal client with an opengl framebuffer object attached. + if (!pixmap->fbo().isNull()) { + return !pixmap->toplevel()->damage().isEmpty(); + } + + // That's an X11 client. + return false; +} + bool OpenGLWindowPixmap::bind() { if (!m_texture->isNull()) { // always call updateBuffer to get the sub-surface tree updated if (subSurface().isNull() && !toplevel()->damage().isEmpty()) { updateBuffer(); } - auto s = surface(); - if (s && !s->trackedDamage().isEmpty()) { + if (needsPixmapUpdate(this)) { m_texture->updateFromPixmap(this); // mipmaps need to be updated m_texture->setDirty(); diff --git a/plugins/scenes/qpainter/scene_qpainter.cpp b/plugins/scenes/qpainter/scene_qpainter.cpp --- a/plugins/scenes/qpainter/scene_qpainter.cpp +++ b/plugins/scenes/qpainter/scene_qpainter.cpp @@ -402,6 +402,11 @@ if (!isValid()) { return; } + if (!surface()) { + // That's an internal client. + m_image = internalImage(); + return; + } // performing deep copy, this could probably be improved m_image = buffer()->data().copy(); if (auto s = surface()) { @@ -419,6 +424,11 @@ const auto oldBuffer = buffer(); WindowPixmap::updateBuffer(); const auto &b = buffer(); + if (!surface()) { + // That's an internal client. + m_image = internalImage(); + return; + } if (b.isNull()) { m_image = QImage(); return; diff --git a/pointer_input.cpp b/pointer_input.cpp --- a/pointer_input.cpp +++ b/pointer_input.cpp @@ -474,7 +474,6 @@ if (old) { // leave internal window - // TODO: do this instead via Wayland protocol as below QEvent leaveEvent(QEvent::Leave); QCoreApplication::sendEvent(old, &leaveEvent); } @@ -548,22 +547,22 @@ workspace()->updateFocusMousePosition(m_pos.toPoint()); } - auto seat = waylandServer()->seat(); - if (!focusNow || !focusNow->surface() || decoration()) { - // no new surface or internal window or on decoration -> cleanup - warpXcbOnSurfaceLeft(nullptr); - seat->setFocusedPointerSurface(nullptr); - return; - } - if (internalWindow()) { // enter internal window - // TODO: do this instead via Wayland protocol as below const auto pos = at()->pos(); QEnterEvent enterEvent(pos, pos, m_pos); QCoreApplication::sendEvent(internalWindow().data(), &enterEvent); } + auto seat = waylandServer()->seat(); + if (!focusNow || !focusNow->surface() || decoration()) { + // Clean up focused pointer surface if there's no client to take focus, + // or the pointer is on a client without surface or on a decoration. + warpXcbOnSurfaceLeft(nullptr); + seat->setFocusedPointerSurface(nullptr); + return; + } + // TODO: add convenient API to update global pos together with updating focused surface warpXcbOnSurfaceLeft(focusNow->surface()); diff --git a/scene.h b/scene.h --- a/scene.h +++ b/scene.h @@ -425,6 +425,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. @@ -512,6 +513,7 @@ QRect m_contentsRect; QPointer m_buffer; QSharedPointer m_fbo; + QImage m_internalImage; WindowPixmap *m_parent = nullptr; QVector m_children; QPointer m_subSurface; @@ -618,6 +620,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 @@ -844,10 +844,8 @@ if (cached_quad_list != nullptr && !force) return *cached_quad_list; WindowQuadList ret; - qreal scale = 1.0; - if (toplevel->surface()) { - scale = toplevel->surface()->scale(); - } + + const qreal scale = toplevel->bufferScale(); if (toplevel->clientPos() == QPoint(0, 0) && toplevel->clientSize() == toplevel->decorationRect().size()) ret = makeQuads(WindowQuadContents, shape(), QPoint(0,0), scale); // has no decoration @@ -1059,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; @@ -1110,13 +1108,11 @@ 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()) { + 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); @@ -140,10 +140,6 @@ QRect transientPlacement(const QRect &bounds) const override; QMatrix4x4 inputTransformation() const override; - - bool setupCompositing() override; - void finishCompositing(ReleaseReason releaseReason = ReleaseReason::Release) override; - void showOnScreenEdge() override; void killWindow() override; @@ -180,19 +176,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; - } - - bool isUnmapped() const { - return m_unmapped; - } - private Q_SLOTS: void clientFullScreenChanged(bool fullScreen); @@ -222,6 +205,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; @@ -267,7 +254,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; @@ -297,7 +283,6 @@ QMargins m_windowMargins; - bool m_compositingSetup = false; bool m_isInitialized = false; friend class Workspace; 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 @@ -71,7 +72,6 @@ , m_shellSurface(surface) , m_xdgShellSurface(nullptr) , m_xdgShellPopup(nullptr) - , m_internal(surface->client() == waylandServer()->internalConnection()) { setSurface(surface->surface()); init(); @@ -83,7 +83,6 @@ , m_shellSurface(nullptr) , m_xdgShellSurface(surface) , m_xdgShellPopup(nullptr) - , m_internal(surface->client() == waylandServer()->internalConnection()) { setSurface(surface->surface()); m_requestGeometryBlockCounter++; @@ -96,7 +95,6 @@ , m_shellSurface(nullptr) , m_xdgShellSurface(nullptr) , m_xdgShellPopup(surface) - , m_internal(surface->client() == waylandServer()->internalConnection()) { setSurface(surface->surface()); m_requestGeometryBlockCounter++; @@ -228,9 +226,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; } @@ -337,7 +333,7 @@ } // set initial desktop - setDesktop(m_internal ? int(NET::OnAllDesktops) : VirtualDesktopManager::self()->current()); + setDesktop(VirtualDesktopManager::self()->current()); // setup shadow integration getShadow(); @@ -1159,9 +1155,7 @@ void ShellClient::createWindowId() { - if (!m_internal) { - m_windowId = waylandServer()->createWindowId(surface()); - } + m_windowId = waylandServer()->createWindowId(surface()); } pid_t ShellClient::pid() const @@ -1179,11 +1173,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; @@ -1220,7 +1214,6 @@ } m_blockedRequestGeometry = QRect(); - return true; } void ShellClient::updatePendingGeometry() @@ -1292,12 +1285,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; @@ -1824,9 +1812,6 @@ bool ShellClient::shouldExposeToWindowManagement() { - if (m_internal) { - return false; - } if (isLockScreen()) { return false; } @@ -1869,21 +1854,6 @@ workspace()->updateMinimizedOfTransients(this); } -bool ShellClient::setupCompositing() -{ - if (m_compositingSetup) { - return true; - } - m_compositingSetup = Toplevel::setupCompositing(); - return m_compositingSetup; -} - -void ShellClient::finishCompositing(ReleaseReason releaseReason) -{ - m_compositingSetup = false; - Toplevel::finishCompositing(releaseReason); -} - void ShellClient::placeIn(const QRect &area) { Placement::self()->place(this, area); @@ -1997,11 +1967,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 @@ -285,6 +285,13 @@ * @since 5.12 */ qreal screenScale() const; // + /** + * Returns the ratio between physical pixels and device-independent pixels for + * the attached buffer (or pixmap). + * + * For X11 clients, this method always returns 1. + */ + virtual qreal bufferScale() const; virtual QPoint clientPos() const = 0; // inside of geometry() /** * Describes how the client's content maps to the window geometry including the frame. @@ -441,8 +448,8 @@ KWayland::Server::SurfaceInterface *surface() const; void setSurface(KWayland::Server::SurfaceInterface *surface); - virtual void setInternalFramebufferObject(const QSharedPointer &fbo); const QSharedPointer &internalFramebufferObject() const; + QImage internalImageObject() const; /** * @returns Transformation to map from global to window coordinates. @@ -626,6 +633,11 @@ bool ready_for_painting; QRegion repaints_region; // updating, repaint just requires repaint of that area QRegion layer_repaints_region; + /** + * An FBO object KWin internal windows might render to. + */ + QSharedPointer m_internalFBO; + QImage m_internalImage; protected: bool m_isDamaged; @@ -649,10 +661,6 @@ bool m_skipCloseAnimation; quint32 m_surfaceId = 0; KWayland::Server::SurfaceInterface *m_surface = nullptr; - /** - * An FBO object KWin internal windows might render to. - */ - QSharedPointer m_internalFBO; // when adding new data members, check also copyToDeleted() qreal m_screenScale = 1.0; }; @@ -919,6 +927,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 @@ -558,6 +558,11 @@ return m_screenScale; } +qreal Toplevel::bufferScale() const +{ + return surface() ? surface()->scale() : 1; +} + bool Toplevel::isOnScreen(int screen) const { return screens()->geometry(screen).intersects(geometry()); @@ -770,15 +775,6 @@ } } -void Toplevel::setInternalFramebufferObject(const QSharedPointer &fbo) -{ - if (m_internalFBO != fbo) { - discardWindowPixmap(); - m_internalFBO = fbo; - } - setDepth(32); -} - QMatrix4x4 Toplevel::inputTransformation() const { QMatrix4x4 m; 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 @@ -277,7 +273,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" @@ -159,12 +158,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); } @@ -183,11 +177,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 { @@ -650,7 +640,6 @@ void WaylandServer::removeClient(ShellClient *c) { m_clients.removeAll(c); - m_internalClients.removeAll(c); emit shellClientRemoved(c); } @@ -699,9 +688,6 @@ if (ShellClient *c = findClientInList(m_clients, id)) { return c; } - if (ShellClient *c = findClientInList(m_internalClients, id)) { - return c; - } return nullptr; } @@ -713,33 +699,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 @@ -5,6 +5,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2009 Lucas Murray +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 @@ -53,6 +54,7 @@ class AbstractClient; class Client; class Compositor; +class InternalClient; class KillWindow; class ShortcutDialog; class UserActionsMenu; @@ -127,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. * @@ -238,6 +235,13 @@ return m_allClients; } + /** + * @returns List of all internal clients currently managed by Workspace + */ + const QList &internalClients() const { + return m_internalClients; + } + void stackScreenEdgesUnderOverrideRedirect(); public: @@ -392,6 +396,26 @@ return client_keys_dialog; } + /** + * Adds the internal client to Workspace. + * + * This method will be called by InternalClient when it's mapped. + * + * @see internalClientAdded + * @internal + */ + void addInternalClient(InternalClient *client); + + /** + * Removes the internal client from Workspace. + * + * This method is meant to be called only by InternalClient. + * + * @see internalClientRemoved + * @internal + */ + void removeInternalClient(InternalClient *client); + public Q_SLOTS: void performWindowOperation(KWin::AbstractClient* c, Options::WindowOperation op); // Keybindings @@ -497,6 +521,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(); @@ -564,6 +598,7 @@ ClientList desktops; UnmanagedList unmanaged; DeletedList deleted; + QList m_internalClients; ToplevelList unconstrained_stacking_order; // Topmost last ToplevelList stacking_order; // Topmost last diff --git a/workspace.cpp b/workspace.cpp --- a/workspace.cpp +++ b/workspace.cpp @@ -4,6 +4,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak +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 @@ -37,6 +38,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" @@ -562,16 +564,15 @@ for (ShellClient *shellClient : shellClients) { shellClient->destroyClient(); } - - const QList internalClients = waylandServer()->internalClients(); - for (ShellClient *internalClient : internalClients) { - internalClient->destroyClient(); - } } for (UnmanagedList::iterator it = unmanaged.begin(), end = unmanaged.end(); it != end; ++it) (*it)->release(ReleaseReason::KWinShutsDown); + for (InternalClient *client : m_internalClients) { + client->destroyClient(); + } + if (auto c = kwinApp()->x11Connection()) { xcb_delete_property(c, kwinApp()->x11RootWindow(), atoms->kwin_running); } @@ -1667,10 +1668,8 @@ if (Client *ret = Toplevel::findInList(desktops, func)) { return ret; } - if (waylandServer()) { - if (AbstractClient *ret = Toplevel::findInList(waylandServer()->internalClients(), func)) { - return ret; - } + if (InternalClient *ret = Toplevel::findInList(m_internalClients, func)) { + return ret; } return nullptr; } @@ -1721,20 +1720,10 @@ if (Unmanaged *ret = Toplevel::findInList(unmanaged, func)) { return ret; } - return nullptr; -} - -Toplevel *Workspace::findToplevel(QWindow *w) const -{ - if (!w) { - return nullptr; - } - if (waylandServer()) { - if (auto c = waylandServer()->findClient(w)) { - return c; - } + if (InternalClient *ret = Toplevel::findInList(m_internalClients, func)) { + return ret; } - return findUnmanaged(w->winId()); + return nullptr; } bool Workspace::hasClient(const AbstractClient *c) @@ -1753,6 +1742,7 @@ { std::for_each(m_allClients.constBegin(), m_allClients.constEnd(), func); std::for_each(desktops.constBegin(), desktops.constEnd(), func); + std::for_each(m_internalClients.constBegin(), m_internalClients.constEnd(), func); } Toplevel *Workspace::findInternal(QWindow *w) const @@ -1762,9 +1752,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 @@ -1804,5 +1798,33 @@ #endif } -} // namespace +void Workspace::addInternalClient(InternalClient *client) +{ + m_internalClients.append(client); + + setupClientConnections(client); + client->updateLayer(); + + if (client->isDecorated()) { + client->keepInArea(clientArea(FullScreenArea, client)); + } + markXStackingOrderAsDirty(); + updateStackingOrder(true); + updateClientArea(); + + emit internalClientAdded(client); +} + +void Workspace::removeInternalClient(InternalClient *client) +{ + m_internalClients.removeOne(client); + + markXStackingOrderAsDirty(); + updateStackingOrder(true); + updateClientArea(); + + emit internalClientRemoved(client); +} + +} // namespace