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
icc-effect-5.14.5
Martin Gräßlin 2016-08-09 13:50:29 +02:00
parent 9b30cfef1d
commit 7bca270f97
6 changed files with 213 additions and 2 deletions

View File

@ -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)

View File

@ -0,0 +1,178 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#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 <kwineffects.h>
#include <netwm.h>
#include <xcb/xcb_icccm.h>
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<KWin::ShellClient*>();
qRegisterMetaType<KWin::AbstractClient*>();
qRegisterMetaType<KWin::Deleted*>();
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<xcb_connection_t, XcbConnectionDeleter> 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<Client*>();
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"

View File

@ -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> surface(Test::createSurface());
QVERIFY(!surface.isNull());
QScopedPointer<ShellSurface> shellSurface(Test::createShellSurface(surface.data()));
QVERIFY(!shellSurface.isNull());
QScopedPointer<PlasmaShellSurface> 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"

View File

@ -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<EffectWindow*>(), client->effectWindow());

View File

@ -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);

View File

@ -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);
}