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