From 7bca270f97a6b13b6dd29951b849cf214f1047b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 9 Aug 2016 13:50:29 +0200 Subject: [PATCH] Force windows of type desktop to be opaque Summary: Plasmashell's desktop windows are RGBA which forces the compositor to perform blending and render the background. That is absolutely pointless as there is no window behind the desktop window it could blend to. All it does is destroying KWin's more optimized code path and forcing additional rendering which will never be visible (including shader push/pop). With this change KWin forces desktop windows (both X11 and Wayland) to be considered as opaque by setting the depth to 24. Thus blending is disabled and the background is not rendered. Test Plan: Verified with apitrace that KWin goes in the opaque rendering path for desktop windows. Reviewers: #kwin, #plasma Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D2382 --- autotests/integration/CMakeLists.txt | 1 + .../integration/desktop_window_x11_test.cpp | 178 ++++++++++++++++++ autotests/integration/plasma_surface_test.cpp | 23 +++ autotests/integration/shell_client_test.cpp | 6 +- manage.cpp | 5 + shell_client.cpp | 2 +- 6 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 autotests/integration/desktop_window_x11_test.cpp diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt index e91bdf7d0f..38ca21eb50 100644 --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -46,6 +46,7 @@ if (XCB_ICCCM_FOUND) integrationTest(NAME testDontCrashAuroraeDestroyDeco SRCS dont_crash_aurorae_destroy_deco.cpp LIBS XCB::ICCCM) integrationTest(NAME testPlasmaWindow SRCS plasmawindow_test.cpp LIBS XCB::ICCCM) integrationTest(NAME testScreenEdgeClientShow SRCS screenedge_client_show_test.cpp LIBS XCB::ICCCM) + integrationTest(NAME testX11DesktopWindow SRCS desktop_window_x11_test.cpp LIBS XCB::ICCCM) endif() add_subdirectory(scripting) diff --git a/autotests/integration/desktop_window_x11_test.cpp b/autotests/integration/desktop_window_x11_test.cpp new file mode 100644 index 0000000000..ca5ceeb289 --- /dev/null +++ b/autotests/integration/desktop_window_x11_test.cpp @@ -0,0 +1,178 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2016 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 "kwin_wayland_test.h" +#include "platform.h" +#include "client.h" +#include "cursor.h" +#include "deleted.h" +#include "screenedge.h" +#include "screens.h" +#include "wayland_server.h" +#include "workspace.h" +#include "shell_client.h" +#include "xcbutils.h" +#include + +#include +#include + +namespace KWin +{ + +static const QString s_socketName = QStringLiteral("wayland_test_kwin_x11_desktop_window-0"); + +class X11DesktopWindowTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + void testDesktopWindow(); + +private: +}; + +void X11DesktopWindowTest::initTestCase() +{ + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); + QVERIFY(workspaceCreatedSpy.isValid()); + kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); + QMetaObject::invokeMethod(kwinApp()->platform(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); + QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); + + kwinApp()->start(); + QVERIFY(workspaceCreatedSpy.wait()); + QCOMPARE(screens()->count(), 2); + QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); + QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); + setenv("QT_QPA_PLATFORM", "wayland", true); + waylandServer()->initWorkspace(); +} + +void X11DesktopWindowTest::init() +{ + screens()->setCurrent(0); + Cursor::setPos(QPoint(640, 512)); +} + +void X11DesktopWindowTest::cleanup() +{ +} + +struct XcbConnectionDeleter +{ + static inline void cleanup(xcb_connection_t *pointer) + { + xcb_disconnect(pointer); + } +}; + +void X11DesktopWindowTest::testDesktopWindow() +{ + // this test creates a desktop window with an RGBA visual and verifies that it's only considered + // as an RGB (opaque) window in KWin + + // create an xcb window + QScopedPointer c(xcb_connect(nullptr, nullptr)); + QVERIFY(!xcb_connection_has_error(c.data())); + + xcb_window_t w = xcb_generate_id(c.data()); + const QRect windowGeometry(0, 0, 1280, 1024); + + // helper to find the visual + auto findDepth = [&c] () -> xcb_visualid_t { + // find a visual with 32 depth + const xcb_setup_t *setup = xcb_get_setup(c.data()); + + for (auto screen = xcb_setup_roots_iterator(setup); screen.rem; xcb_screen_next(&screen)) { + for (auto depth = xcb_screen_allowed_depths_iterator(screen.data); depth.rem; xcb_depth_next(&depth)) { + if (depth.data->depth != 32) { + continue; + } + const int len = xcb_depth_visuals_length(depth.data); + const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data); + + for (int i = 0; i < len; i++) { + return visuals[0].visual_id; + } + } + } + return 0; + }; + auto visualId = findDepth(); + auto colormapId = xcb_generate_id(c.data()); + auto cmCookie = xcb_create_colormap_checked(c.data(), XCB_COLORMAP_ALLOC_NONE, colormapId, rootWindow(), visualId); + QVERIFY(!xcb_request_check(c.data(), cmCookie)); + + const uint32_t values[] = {XCB_PIXMAP_NONE, defaultScreen()->black_pixel, colormapId}; + auto cookie = xcb_create_window_checked(c.data(), 32, w, rootWindow(), + windowGeometry.x(), + windowGeometry.y(), + windowGeometry.width(), + windowGeometry.height(), + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visualId, XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, values); + QVERIFY(!xcb_request_check(c.data(), cookie)); + xcb_size_hints_t hints; + memset(&hints, 0, sizeof(hints)); + xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); + xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); + xcb_icccm_set_wm_normal_hints(c.data(), w, &hints); + NETWinInfo info(c.data(), w, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties); + info.setWindowType(NET::Desktop); + xcb_map_window(c.data(), w); + xcb_flush(c.data()); + + // verify through a geometry request that it's depth 32 + Xcb::WindowGeometry geo(w); + QCOMPARE(geo->depth, uint8_t(32)); + + // we should get a client for it + QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); + QVERIFY(windowCreatedSpy.isValid()); + QVERIFY(windowCreatedSpy.wait()); + Client *client = windowCreatedSpy.first().first().value(); + QVERIFY(client); + QCOMPARE(client->window(), w); + QVERIFY(!client->isDecorated()); + QCOMPARE(client->windowType(), NET::Desktop); + QCOMPARE(client->geometry(), windowGeometry); + QVERIFY(client->isDesktop()); + QCOMPARE(client->depth(), 24); + QVERIFY(!client->hasAlpha()); + + // and destroy the window again + xcb_unmap_window(c.data(), w); + xcb_destroy_window(c.data(), w); + xcb_flush(c.data()); + c.reset(); + + QSignalSpy windowClosedSpy(client, &Client::windowClosed); + QVERIFY(windowClosedSpy.isValid()); + QVERIFY(windowClosedSpy.wait()); +} + +} + +WAYLANDTEST_MAIN(KWin::X11DesktopWindowTest) +#include "desktop_window_x11_test.moc" diff --git a/autotests/integration/plasma_surface_test.cpp b/autotests/integration/plasma_surface_test.cpp index caf55c5551..819de4c0c6 100644 --- a/autotests/integration/plasma_surface_test.cpp +++ b/autotests/integration/plasma_surface_test.cpp @@ -49,6 +49,8 @@ private Q_SLOTS: void testAcceptsFocus_data(); void testAcceptsFocus(); + void testDesktopIsOpaque(); + private: ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; @@ -176,5 +178,26 @@ void PlasmaSurfaceTest::testAcceptsFocus() QTEST(c->isActive(), "active"); } +void PlasmaSurfaceTest::testDesktopIsOpaque() +{ + QScopedPointer surface(Test::createSurface()); + QVERIFY(!surface.isNull()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); + QVERIFY(!shellSurface.isNull()); + QScopedPointer plasmaSurface(m_plasmaShell->createSurface(surface.data())); + QVERIFY(!plasmaSurface.isNull()); + plasmaSurface->setRole(PlasmaShellSurface::Role::Desktop); + + // now render to map the window + auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + + QVERIFY(c); + QCOMPARE(c->windowType(), NET::Desktop); + QVERIFY(c->isDesktop()); + + QVERIFY(!c->hasAlpha()); + QCOMPARE(c->depth(), 24); +} + WAYLANDTEST_MAIN(PlasmaSurfaceTest) #include "plasma_surface_test.moc" diff --git a/autotests/integration/shell_client_test.cpp b/autotests/integration/shell_client_test.cpp index 6c60737aad..61c16393e0 100644 --- a/autotests/integration/shell_client_test.cpp +++ b/autotests/integration/shell_client_test.cpp @@ -115,6 +115,8 @@ void TestShellClient::testMapUnmapMap() QVERIFY(client->isShown(true)); QCOMPARE(client->isHiddenInternal(), false); QCOMPARE(client->readyForPainting(), true); + QCOMPARE(client->depth(), 32); + QVERIFY(client->hasAlpha()); QCOMPARE(workspace()->activeClient(), client); QVERIFY(effectsWindowShownSpy.isEmpty()); @@ -135,13 +137,15 @@ void TestShellClient::testMapUnmapMap() QSignalSpy windowShownSpy(client, &ShellClient::windowShown); QVERIFY(windowShownSpy.isValid()); - Test::render(surface.data(), QSize(100, 50), Qt::blue); + Test::render(surface.data(), QSize(100, 50), Qt::blue, QImage::Format_RGB32); QCOMPARE(clientAddedSpy.count(), 1); QVERIFY(windowShownSpy.wait()); QCOMPARE(windowShownSpy.count(), 1); QCOMPARE(clientAddedSpy.count(), 1); QCOMPARE(client->readyForPainting(), true); QCOMPARE(client->isHiddenInternal(), false); + QCOMPARE(client->depth(), 24); + QVERIFY(!client->hasAlpha()); QCOMPARE(workspace()->activeClient(), client); QCOMPARE(effectsWindowShownSpy.count(), 1); QCOMPARE(effectsWindowShownSpy.first().first().value(), client->effectWindow()); diff --git a/manage.cpp b/manage.cpp index 4198e60436..67abd95c79 100644 --- a/manage.cpp +++ b/manage.cpp @@ -107,6 +107,11 @@ bool Client::manage(xcb_window_t w, bool isMapped) m_motif.init(window()); info = new WinInfo(this, m_client, rootWindow(), properties, properties2); + if (isDesktop() && bit_depth == 32) { + // force desktop windows to be opaque. It's a desktop after all, there is no window below + bit_depth = 24; + } + // If it's already mapped, ignore hint bool init_minimize = !isMapped && (info->initialMappingState() == NET::Iconic); diff --git a/shell_client.cpp b/shell_client.cpp index 1e29efca91..ac9dfdd8ec 100644 --- a/shell_client.cpp +++ b/shell_client.cpp @@ -368,7 +368,7 @@ void ShellClient::addDamage(const QRegion &damage) doSetGeometry(QRect(position, m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); } markAsMapped(); - setDepth(s->buffer()->hasAlphaChannel() ? 32 : 24); + setDepth((s->buffer()->hasAlphaChannel() && !isDesktop()) ? 32 : 24); repaints_region += damage.translated(clientPos()); Toplevel::addDamage(damage); }