diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index cb787122a..9cdf7962b 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,309 +1,310 @@ add_definitions(-DKWIN_UNIT_TEST) remove_definitions(-DQT_USE_QSTRINGBUILDER) +add_subdirectory(libxrenderutils) add_subdirectory(wayland) if (HAVE_INPUT) add_subdirectory(libinput) endif() ######################################################## # Test ScreenPaintData ######################################################## set( testScreenPaintData_SRCS test_screen_paint_data.cpp ) add_executable(testScreenPaintData ${testScreenPaintData_SRCS}) target_link_libraries( testScreenPaintData kwineffects Qt5::Test Qt5::Widgets KF5::WindowSystem) add_test(kwin-testScreenPaintData testScreenPaintData) ecm_mark_as_test(testScreenPaintData) ######################################################## # Test WindowPaintData ######################################################## set( testWindowPaintData_SRCS test_window_paint_data.cpp ) add_executable(testWindowPaintData ${testWindowPaintData_SRCS}) target_link_libraries( testWindowPaintData kwineffects Qt5::Widgets Qt5::Test ) add_test(kwin-testWindowPaintData testWindowPaintData) ecm_mark_as_test(testWindowPaintData) ######################################################## # Test VirtualDesktopManager ######################################################## set( testVirtualDesktops_SRCS test_virtual_desktops.cpp ../virtualdesktops.cpp ) add_executable(testVirtualDesktops ${testVirtualDesktops_SRCS}) target_link_libraries( testVirtualDesktops Qt5::Test Qt5::Widgets KF5::I18n KF5::GlobalAccel KF5::ConfigCore KF5::WindowSystem ) add_test(kwin-testVirtualDesktops testVirtualDesktops) ecm_mark_as_test(testVirtualDesktops) ######################################################## # Test ClientMachine ######################################################## set( testClientMachine_SRCS test_client_machine.cpp ../client_machine.cpp ) add_executable( testClientMachine ${testClientMachine_SRCS} ) target_link_libraries( testClientMachine Qt5::Concurrent Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::XFIXES ${X11_X11_LIB} # to make jenkins happy ) add_test(kwin-testClientMachine testClientMachine) ecm_mark_as_test(testClientMachine) ######################################################## # Test XcbWrapper ######################################################## set( testXcbWrapper_SRCS test_xcb_wrapper.cpp ) add_executable( testXcbWrapper ${testXcbWrapper_SRCS} ) target_link_libraries( testXcbWrapper Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(kwin-testXcbWrapper testXcbWrapper) ecm_mark_as_test(testXcbWrapper) if (XCB_ICCCM_FOUND) add_executable( testXcbSizeHints test_xcb_size_hints.cpp ) target_link_libraries( testXcbSizeHints Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::ICCCM ) add_test(kwin-testXcbSizeHints testXcbSizeHints) ecm_mark_as_test(testXcbSizeHints) endif() ######################################################## # Test XcbWindow ######################################################## set( testXcbWindow_SRCS test_xcb_window.cpp ) add_executable( testXcbWindow ${testXcbWindow_SRCS} ) target_link_libraries( testXcbWindow Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(kwin-testXcbWindow testXcbWindow) ecm_mark_as_test(testXcbWindow) ######################################################## # Test BuiltInEffectLoader ######################################################## set( testBuiltInEffectLoader_SRCS test_builtin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testBuiltInEffectLoader ${testBuiltInEffectLoader_SRCS}) target_link_libraries(testBuiltInEffectLoader Qt5::Concurrent Qt5::Test KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testBuiltInEffectLoader testBuiltInEffectLoader) ecm_mark_as_test(testBuiltInEffectLoader) ######################################################## # Test ScriptedEffectLoader ######################################################## include_directories(${KWIN_SOURCE_DIR}) set( testScriptedEffectLoader_SRCS test_scripted_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ../scripting/scriptedeffect.cpp ../scripting/scriptingutils.cpp ../scripting/scripting_logging.cpp ) add_executable( testScriptedEffectLoader ${testScriptedEffectLoader_SRCS}) target_link_libraries(testScriptedEffectLoader Qt5::Concurrent Qt5::Script Qt5::Test KF5::ConfigGui KF5::GlobalAccel KF5::I18n KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testScriptedEffectLoader testScriptedEffectLoader) ecm_mark_as_test(testScriptedEffectLoader) ######################################################## # Test PluginEffectLoader ######################################################## set( testPluginEffectLoader_SRCS test_plugin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testPluginEffectLoader ${testPluginEffectLoader_SRCS}) target_link_libraries(testPluginEffectLoader Qt5::Concurrent Qt5::Test KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testPluginEffectLoader testPluginEffectLoader) ecm_mark_as_test(testPluginEffectLoader) ######################################################## # FakeEffectPlugin ######################################################## add_library(fakeeffectplugin MODULE fakeeffectplugin.cpp) set_target_properties(fakeeffectplugin PROPERTIES PREFIX "") target_link_libraries(fakeeffectplugin kwineffects) ######################################################## # FakeEffectPlugin-Version ######################################################## add_library(effectversionplugin MODULE fakeeffectplugin_version.cpp) set_target_properties(effectversionplugin PROPERTIES PREFIX "") target_link_libraries(effectversionplugin kwineffects) ######################################################## # Test Screens ######################################################## set( testScreens_SRCS test_screens.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../screens.cpp ../x11eventfilter.cpp ) kconfig_add_kcfg_files(testScreens_SRCS ../settings.kcfgc) add_executable( testScreens ${testScreens_SRCS}) target_include_directories(testScreens BEFORE PRIVATE ./) target_link_libraries(testScreens Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::ConfigGui KF5::WindowSystem ) add_test(kwin_testScreens testScreens) ecm_mark_as_test(testScreens) ######################################################## # Test XrandRScreens ######################################################## set( testXRandRScreens_SRCS test_xrandr_screens.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../screens.cpp ../plugins/platforms/x11/standalone/screens_xrandr.cpp ../xcbutils.cpp # init of extensions ../x11eventfilter.cpp ) kconfig_add_kcfg_files(testXRandRScreens_SRCS ../settings.kcfgc) add_executable( testXRandRScreens ${testXRandRScreens_SRCS} ) target_link_libraries( testXRandRScreens Qt5::Test Qt5::Gui KF5::ConfigCore KF5::ConfigGui KF5::WindowSystem XCB::XCB XCB::RANDR XCB::XFIXES XCB::SYNC XCB::COMPOSITE XCB::DAMAGE XCB::GLX XCB::SHM ) add_test(kwin-testXRandRScreens testXRandRScreens) ecm_mark_as_test(testXRandRScreens) ######################################################## # Test ScreenEdges ######################################################## set( testScreenEdges_SRCS test_screen_edges.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../atoms.cpp ../screens.cpp ../screenedge.cpp ../virtualdesktops.cpp ../xcbutils.cpp # init of extensions ../plugins/platforms/x11/standalone/edge.cpp ) kconfig_add_kcfg_files(testScreenEdges_SRCS ../settings.kcfgc) qt5_add_dbus_interface( testScreenEdges_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.freedesktop.ScreenSaver.xml screenlocker_interface) add_executable( testScreenEdges ${testScreenEdges_SRCS}) target_include_directories(testScreenEdges BEFORE PRIVATE ./) target_link_libraries(testScreenEdges Qt5::DBus Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::GlobalAccel KF5::WindowSystem XCB::XCB XCB::RANDR XCB::XFIXES XCB::SYNC XCB::COMPOSITE XCB::DAMAGE XCB::GLX XCB::SHM ) add_test(kwin_testScreenEdges testScreenEdges) ecm_mark_as_test(testScreenEdges) diff --git a/autotests/libxrenderutils/CMakeLists.txt b/autotests/libxrenderutils/CMakeLists.txt new file mode 100644 index 000000000..b73291c2e --- /dev/null +++ b/autotests/libxrenderutils/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(blendPictureTest blendpicture_test.cpp) +target_link_libraries(blendPictureTest + kwinxrenderutils + Qt5::Test + Qt5::Gui + Qt5::X11Extras + XCB::XCB + XCB::RENDER + XCB::XFIXES + ) +ecm_mark_as_test(blendPictureTest) diff --git a/autotests/libxrenderutils/blendpicture_test.cpp b/autotests/libxrenderutils/blendpicture_test.cpp new file mode 100644 index 000000000..d00ad3df7 --- /dev/null +++ b/autotests/libxrenderutils/blendpicture_test.cpp @@ -0,0 +1,59 @@ +/******************************************************************** +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 +#include +#include + +#include "../../libkwineffects/kwinxrenderutils.h" + +class BlendPictureTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void testDontCrashOnTeardown(); +}; + +void BlendPictureTest::initTestCase() +{ + KWin::XRenderUtils::init(QX11Info::connection(), QX11Info::appRootWindow()); +} + +void BlendPictureTest::cleanupTestCase() +{ + KWin::XRenderUtils::cleanup(); +} + +void BlendPictureTest::testDontCrashOnTeardown() +{ + // this test uses xrenderBlendPicture - the only idea is to trigger the creation + // closing the application should not crash + // see BUG 363251 + const auto picture = KWin::xRenderBlendPicture(0.5); + // and a second one + const auto picture2 = KWin::xRenderBlendPicture(0.6); + Q_UNUSED(picture) + Q_UNUSED(picture2) +} + +QTEST_MAIN(BlendPictureTest) +#include "blendpicture_test.moc" diff --git a/libkwineffects/kwinxrenderutils.cpp b/libkwineffects/kwinxrenderutils.cpp index da8c24990..1c2108b07 100644 --- a/libkwineffects/kwinxrenderutils.cpp +++ b/libkwineffects/kwinxrenderutils.cpp @@ -1,286 +1,296 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Lubos Lunak 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 "kwinxrenderutils.h" #include "logging_p.h" +#include #include #include #include namespace KWin { namespace XRenderUtils { static xcb_connection_t *s_connection = nullptr; static xcb_window_t s_rootWindow = XCB_WINDOW_NONE; +static XRenderPicture s_blendPicture(XCB_RENDER_PICTURE_NONE); void init(xcb_connection_t *connection, xcb_window_t rootWindow) { s_connection = connection; s_rootWindow = rootWindow; } +void cleanup() +{ + s_blendPicture = XRenderPicture(XCB_RENDER_PICTURE_NONE); + s_connection = nullptr; + s_rootWindow = XCB_WINDOW_NONE; +} + } // namespace // adapted from Qt, because this really sucks ;) xcb_render_color_t preMultiply(const QColor &c, float opacity) { xcb_render_color_t color; const uint A = c.alpha() * opacity, R = c.red(), G = c.green(), B = c.blue(); color.alpha = (A | A << 8); color.red = (R | R << 8) * color.alpha / 0x10000; color.green = (G | G << 8) * color.alpha / 0x10000; color.blue = (B | B << 8) * color.alpha / 0x10000; return color; } XRenderPicture xRenderFill(const xcb_render_color_t &c) { xcb_pixmap_t pixmap = xcb_generate_id(XRenderUtils::s_connection); xcb_create_pixmap(XRenderUtils::s_connection, 32, pixmap, XRenderUtils::s_rootWindow, 1, 1); XRenderPicture fill(pixmap, 32); xcb_free_pixmap(XRenderUtils::s_connection, pixmap); uint32_t values[] = {true}; xcb_render_change_picture(XRenderUtils::s_connection, fill, XCB_RENDER_CP_REPEAT, values); xcb_rectangle_t rect = {0, 0, 1, 1}; xcb_render_fill_rectangles(XRenderUtils::s_connection, XCB_RENDER_PICT_OP_SRC, fill, c, 1, &rect); return fill; } XRenderPicture xRenderFill(const QColor &c) { return xRenderFill(preMultiply(c)); } XRenderPicture xRenderBlendPicture(double opacity) { - static XRenderPicture s_blendPicture(XCB_RENDER_PICTURE_NONE); static xcb_render_color_t s_blendColor = {0, 0, 0, 0}; s_blendColor.alpha = uint16_t(opacity * 0xffff); - if (s_blendPicture == XCB_RENDER_PICTURE_NONE) { - s_blendPicture = xRenderFill(s_blendColor); + if (XRenderUtils::s_blendPicture == XCB_RENDER_PICTURE_NONE) { + XRenderUtils::s_blendPicture = xRenderFill(s_blendColor); } else { xcb_rectangle_t rect = {0, 0, 1, 1}; - xcb_render_fill_rectangles(XRenderUtils::s_connection, XCB_RENDER_PICT_OP_SRC, s_blendPicture, s_blendColor, 1, &rect); + xcb_render_fill_rectangles(XRenderUtils::s_connection, XCB_RENDER_PICT_OP_SRC, XRenderUtils::s_blendPicture, s_blendColor, 1, &rect); } - return s_blendPicture; + return XRenderUtils::s_blendPicture; } static xcb_render_picture_t createPicture(xcb_pixmap_t pix, int depth) { if (pix == XCB_PIXMAP_NONE) return XCB_RENDER_PICTURE_NONE; xcb_connection_t *c = XRenderUtils::s_connection; static QHash s_renderFormats; if (!s_renderFormats.contains(depth)) { xcb_render_query_pict_formats_reply_t *formats = xcb_render_query_pict_formats_reply(c, xcb_render_query_pict_formats_unchecked(c), nullptr); if (!formats) { return XCB_RENDER_PICTURE_NONE; } for (xcb_render_pictforminfo_iterator_t it = xcb_render_query_pict_formats_formats_iterator(formats); it.rem; xcb_render_pictforminfo_next(&it)) { if (it.data->depth == depth) { s_renderFormats.insert(depth, it.data->id); break; } } free(formats); } QHash::const_iterator it = s_renderFormats.constFind(depth); if (it == s_renderFormats.constEnd()) { qCWarning(LIBKWINXRENDERUTILS) << "Could not find XRender format for depth" << depth; return XCB_RENDER_PICTURE_NONE; } xcb_render_picture_t pic = xcb_generate_id(c); xcb_render_create_picture(c, pic, pix, it.value(), 0, nullptr); return pic; } XRenderPicture::XRenderPicture(const QImage &img) { fromImage(img); } void XRenderPicture::fromImage(const QImage &img) { xcb_connection_t *c = XRenderUtils::s_connection; const int depth = img.depth(); xcb_pixmap_t xpix = xcb_generate_id(c); xcb_create_pixmap(c, depth, xpix, XRenderUtils::s_rootWindow, img.width(), img.height()); xcb_gcontext_t cid = xcb_generate_id(c); xcb_create_gc(c, cid, xpix, 0, nullptr); xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, xpix, cid, img.width(), img.height(), 0, 0, 0, depth, img.byteCount(), img.constBits()); xcb_free_gc(c, cid); d = new XRenderPictureData(createPicture(xpix, depth)); xcb_free_pixmap(c, xpix); } XRenderPicture::XRenderPicture(xcb_pixmap_t pix, int depth) : d(new XRenderPictureData(createPicture(pix, depth))) { } XRenderPictureData::~XRenderPictureData() { - if (picture != XCB_RENDER_PICTURE_NONE) + if (picture != XCB_RENDER_PICTURE_NONE) { + Q_ASSERT(qApp); xcb_render_free_picture(XRenderUtils::s_connection, picture); + } } XFixesRegion::XFixesRegion(const QRegion ®ion) { m_region = xcb_generate_id(XRenderUtils::s_connection); QVector< QRect > rects = region.rects(); QVector< xcb_rectangle_t > xrects(rects.count()); for (int i = 0; i < rects.count(); ++i) { const QRect &rect = rects.at(i); xcb_rectangle_t xrect; xrect.x = rect.x(); xrect.y = rect.y(); xrect.width = rect.width(); xrect.height = rect.height(); xrects[i] = xrect; } xcb_xfixes_create_region(XRenderUtils::s_connection, m_region, xrects.count(), xrects.constData()); } XFixesRegion::~XFixesRegion() { xcb_xfixes_destroy_region(XRenderUtils::s_connection, m_region); } static xcb_render_picture_t s_offscreenTarget = XCB_RENDER_PICTURE_NONE; static QStack s_scene_offscreenTargetStack; static int s_renderOffscreen = 0; void scene_setXRenderOffscreenTarget(xcb_render_picture_t pix) { s_offscreenTarget = pix; } XRenderPicture *scene_xRenderOffscreenTarget() { return s_scene_offscreenTargetStack.isEmpty() ? nullptr : s_scene_offscreenTargetStack.top(); } void setXRenderOffscreen(bool b) { b ? ++s_renderOffscreen : --s_renderOffscreen; if (s_renderOffscreen < 0) { s_renderOffscreen = 0; qCWarning(LIBKWINXRENDERUTILS) << "*** SOMETHING IS MESSED UP WITH YOUR setXRenderOffscreen() USAGE ***"; } } void xRenderPushTarget(XRenderPicture *pic) { s_scene_offscreenTargetStack.push(pic); ++s_renderOffscreen; } void xRenderPopTarget() { s_scene_offscreenTargetStack.pop(); --s_renderOffscreen; if (s_renderOffscreen < 0) { s_renderOffscreen = 0; qCWarning(LIBKWINXRENDERUTILS) << "*** SOMETHING IS MESSED UP WITH YOUR xRenderPopTarget() USAGE ***"; } } bool xRenderOffscreen() { return s_renderOffscreen; } xcb_render_picture_t xRenderOffscreenTarget() { return s_offscreenTarget; } namespace XRenderUtils { struct PictFormatData { PictFormatData() { // Fetch the render pict formats reply = xcb_render_query_pict_formats_reply(s_connection, xcb_render_query_pict_formats_unchecked(s_connection), nullptr); // Init the visual ID -> format ID hash table for (auto screens = xcb_render_query_pict_formats_screens_iterator(reply); screens.rem; xcb_render_pictscreen_next(&screens)) { for (auto depths = xcb_render_pictscreen_depths_iterator(screens.data); depths.rem; xcb_render_pictdepth_next(&depths)) { const xcb_render_pictvisual_t *visuals = xcb_render_pictdepth_visuals(depths.data); const int len = xcb_render_pictdepth_visuals_length(depths.data); for (int i = 0; i < len; i++) visualHash.insert(visuals[i].visual, visuals[i].format); } } // Init the format ID -> xcb_render_directformat_t* hash table const xcb_render_pictforminfo_t *formats = xcb_render_query_pict_formats_formats(reply); const int len = xcb_render_query_pict_formats_formats_length(reply); for (int i = 0; i < len; i++) { if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT) formatInfoHash.insert(formats[i].id, &formats[i].direct); } } ~PictFormatData() { free(reply); } xcb_render_query_pict_formats_reply_t *reply; QHash visualHash; QHash formatInfoHash; }; Q_GLOBAL_STATIC(PictFormatData, g_pictFormatData) xcb_render_pictformat_t findPictFormat(xcb_visualid_t visual) { PictFormatData *d = g_pictFormatData; return d->visualHash.value(visual); } const xcb_render_directformat_t *findPictFormatInfo(xcb_render_pictformat_t format) { PictFormatData *d = g_pictFormatData; return d->formatInfoHash.value(format); } } // namespace XRenderUtils } // namespace KWin diff --git a/libkwineffects/kwinxrenderutils.h b/libkwineffects/kwinxrenderutils.h index f24f90d5c..2413fa107 100644 --- a/libkwineffects/kwinxrenderutils.h +++ b/libkwineffects/kwinxrenderutils.h @@ -1,189 +1,194 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Lubos Lunak 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_XRENDERUTILS_H #define KWIN_XRENDERUTILS_H // KWin #include // Qt #include #include #include // XCB #include class QColor; class QPixmap; /** @addtogroup kwineffects */ /** @{ */ namespace KWin { /** * dumps a QColor into a xcb_render_color_t */ KWINXRENDERUTILS_EXPORT xcb_render_color_t preMultiply(const QColor &c, float opacity = 1.0); /** @internal */ class KWINXRENDERUTILS_EXPORT XRenderPictureData : public QSharedData { public: explicit XRenderPictureData(xcb_render_picture_t pic = XCB_RENDER_PICTURE_NONE); ~XRenderPictureData(); xcb_render_picture_t value(); private: xcb_render_picture_t picture; Q_DISABLE_COPY(XRenderPictureData) }; /** * @short Wrapper around XRender Picture. * * This class wraps XRender's Picture, providing proper initialization, * convenience constructors and freeing of resources. * It should otherwise act exactly like the Picture type. */ class KWINXRENDERUTILS_EXPORT XRenderPicture { public: explicit XRenderPicture(xcb_render_picture_t pic = XCB_RENDER_PICTURE_NONE); explicit XRenderPicture(const QImage &img); XRenderPicture(xcb_pixmap_t pix, int depth); operator xcb_render_picture_t(); private: void fromImage(const QImage &img); QExplicitlySharedDataPointer< XRenderPictureData > d; }; class KWINXRENDERUTILS_EXPORT XFixesRegion { public: explicit XFixesRegion(const QRegion ®ion); virtual ~XFixesRegion(); operator xcb_xfixes_region_t(); private: xcb_xfixes_region_t m_region; }; inline XRenderPictureData::XRenderPictureData(xcb_render_picture_t pic) : picture(pic) { } inline xcb_render_picture_t XRenderPictureData::value() { return picture; } inline XRenderPicture::XRenderPicture(xcb_render_picture_t pic) : d(new XRenderPictureData(pic)) { } inline XRenderPicture::operator xcb_render_picture_t() { return d->value(); } inline XFixesRegion::operator xcb_xfixes_region_t() { return m_region; } /** * Static 1x1 picture used to deliver a black pixel with given opacity (for blending performance) * Call and Use, the PixelPicture will stay, but may change it's opacity meanwhile. It's NOT threadsafe either */ KWINXRENDERUTILS_EXPORT XRenderPicture xRenderBlendPicture(double opacity); /** * Creates a 1x1 Picture filled with c */ KWINXRENDERUTILS_EXPORT XRenderPicture xRenderFill(const xcb_render_color_t &c); KWINXRENDERUTILS_EXPORT XRenderPicture xRenderFill(const QColor &c); /** * Allows to render a window into a (transparent) pixmap * NOTICE: the result can be queried as xRenderWindowOffscreenTarget() * NOTICE: it may be 0 * NOTICE: when done call setXRenderWindowOffscreen(false) to continue normal render process */ KWINXRENDERUTILS_EXPORT void setXRenderOffscreen(bool b); /** * Allows to define a persistent effect member as render target * The window (including shadows) is rendered into the top left corner * NOTICE: do NOT call setXRenderOffscreen(true) in addition! * NOTICE: do not forget to xRenderPopTarget once you're done to continue the normal render process */ KWINXRENDERUTILS_EXPORT void xRenderPushTarget(XRenderPicture *pic); KWINXRENDERUTILS_EXPORT void xRenderPopTarget(); /** * Whether windows are currently rendered into an offscreen target buffer */ KWINXRENDERUTILS_EXPORT bool xRenderOffscreen(); /** * The offscreen buffer as set by the renderer because of setXRenderWindowOffscreen(true) */ KWINXRENDERUTILS_EXPORT xcb_render_picture_t xRenderOffscreenTarget(); /** * NOTICE: HANDS OFF!!! * scene_setXRenderWindowOffscreenTarget() is ONLY to be used by the renderer - DO NOT TOUCH! */ KWINXRENDERUTILS_EXPORT void scene_setXRenderOffscreenTarget(xcb_render_picture_t pix); /** * scene_xRenderWindowOffscreenTarget() is used by the scene to figure the target set by an effect */ KWINXRENDERUTILS_EXPORT XRenderPicture *scene_xRenderOffscreenTarget(); namespace XRenderUtils { /** * @internal **/ KWINXRENDERUTILS_EXPORT void init(xcb_connection_t *connection, xcb_window_t rootWindow); /** * Returns the Xrender format that corresponds to the given visual ID. */ KWINXRENDERUTILS_EXPORT xcb_render_pictformat_t findPictFormat(xcb_visualid_t visual); /** * Returns the xcb_render_directformat_t for the given Xrender format. */ KWINXRENDERUTILS_EXPORT const xcb_render_directformat_t *findPictFormatInfo(xcb_render_pictformat_t format); +/** + * @internal + **/ +KWINXRENDERUTILS_EXPORT void cleanup(); + } // namespace XRenderUtils } // namespace KWin /** @} */ #endif diff --git a/workspace.cpp b/workspace.cpp index a179889b2..85fc17057 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -1,1710 +1,1713 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak 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 . *********************************************************************/ // own #include "workspace.h" // kwin libs #include #include // kwin #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif #include "atoms.h" #include "client.h" #include "composite.h" #include "cursor.h" #include "dbusinterface.h" #include "deleted.h" #include "effects.h" #include "focuschain.h" #include "group.h" #include "input.h" #include "logind.h" #include "killwindow.h" #include "netinfo.h" #include "outline.h" #include "placement.h" #include "rules.h" #include "screenedge.h" #include "screens.h" #include "scripting/scripting.h" #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif #include "unmanaged.h" #include "useractions.h" #include "virtualdesktops.h" #include "shell_client.h" #include "wayland_server.h" #include "xcbutils.h" #include "main.h" #include "decorations/decorationbridge.h" // KDE #include #include #include #include // Qt #include namespace KWin { extern int screen_number; extern bool is_multihead; ColorMapper::ColorMapper(QObject *parent) : QObject(parent) , m_default(defaultScreen()->default_colormap) , m_installed(defaultScreen()->default_colormap) { } ColorMapper::~ColorMapper() { } void ColorMapper::update() { xcb_colormap_t cmap = m_default; if (Client *c = dynamic_cast(Workspace::self()->activeClient())) { if (c->colormap() != XCB_COLORMAP_NONE) { cmap = c->colormap(); } } if (cmap != m_installed) { xcb_install_colormap(connection(), cmap); m_installed = cmap; } } Workspace* Workspace::_self = 0; Workspace::Workspace(const QString &sessionKey) : QObject(0) , m_compositor(NULL) // Unsorted , active_popup(NULL) , active_popup_client(NULL) , m_initialDesktop(1) , active_client(0) , last_active_client(0) , most_recently_raised(0) , movingClient(0) , delayfocus_client(0) , force_restacking(false) , x_stacking_dirty(true) , showing_desktop(false) , was_user_interaction(false) , session_saving(false) , block_focus(0) , m_userActionsMenu(new UserActionsMenu(this)) , client_keys_dialog(NULL) , client_keys_client(NULL) , global_shortcuts_disabled_for_client(false) , workspaceInit(true) , startup(0) , set_active_client_recursion(0) , block_stacking_updates(0) { // If KWin was already running it saved its configuration after loosing the selection -> Reread QFuture reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); _self = this; // first initialize the extensions Xcb::Extensions::self(); #ifdef KWIN_BUILD_ACTIVITIES Activities *activities = nullptr; if (kwinApp()->usesKActivities()) { activities = Activities::create(this); } if (activities) { connect(activities, SIGNAL(currentChanged(QString)), SLOT(updateCurrentActivity(QString))); } #endif // PluginMgr needs access to the config file, so we need to wait for it for finishing reparseConfigFuture.waitForFinished(); options->loadConfig(); options->loadCompositingConfig(false); ColorMapper *colormaps = new ColorMapper(this); connect(this, &Workspace::clientActivated, colormaps, &ColorMapper::update); delayFocusTimer = 0; if (!sessionKey.isEmpty()) loadSessionInfo(sessionKey); connect(qApp, &QGuiApplication::commitDataRequest, this, &Workspace::commitData); connect(qApp, &QGuiApplication::saveStateRequest, this, &Workspace::saveState); RuleBook::create(this)->load(); // Call this before XSelectInput() on the root window startup = new KStartupInfo( KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this); // Select windowmanager privileges selectWmInputEventMask(); ScreenEdges::create(this); // VirtualDesktopManager needs to be created prior to init shortcuts // and prior to TabBox, due to TabBox connecting to signals // actual initialization happens in init() VirtualDesktopManager::create(this); #ifdef KWIN_BUILD_TABBOX // need to create the tabbox before compositing scene is setup TabBox::TabBox::create(this); #endif // init XRenderUtils if (kwinApp()->operationMode() == Application::OperationModeX11) { XRenderUtils::init(connection(), rootWindow()); } if (Compositor::self()) { m_compositor = Compositor::self(); } else { m_compositor = Compositor::create(this); } connect(this, &Workspace::currentDesktopChanged, m_compositor, &Compositor::addRepaintFull); connect(m_compositor, &QObject::destroyed, this, [this] { m_compositor = nullptr; }); auto decorationBridge = Decoration::DecorationBridge::create(this); decorationBridge->init(); connect(this, &Workspace::configChanged, decorationBridge, &Decoration::DecorationBridge::reconfigure); new DBusInterface(this); // Compatibility int32_t data = 1; xcb_change_property(connection(), XCB_PROP_MODE_APPEND, rootWindow(), atoms->kwin_running, atoms->kwin_running, 32, 1, &data); Outline::create(this); initShortcuts(); init(); } void Workspace::init() { updateXTime(); // Needed for proper initialization of user_time in Client ctor KSharedConfigPtr config = kwinApp()->config(); kwinApp()->createScreens(); Screens *screens = Screens::self(); // get screen support connect(screens, SIGNAL(changed()), SLOT(desktopResized())); screens->setConfig(config); screens->reconfigure(); connect(options, SIGNAL(configChanged()), screens, SLOT(reconfigure())); ScreenEdges *screenEdges = ScreenEdges::self(); screenEdges->setConfig(config); screenEdges->init(); connect(options, SIGNAL(configChanged()), screenEdges, SLOT(reconfigure())); connect(VirtualDesktopManager::self(), SIGNAL(layoutChanged(int,int)), screenEdges, SLOT(updateLayout())); connect(this, &Workspace::clientActivated, screenEdges, &ScreenEdges::checkBlocking); FocusChain *focusChain = FocusChain::create(this); connect(this, &Workspace::clientRemoved, focusChain, &FocusChain::remove); connect(this, &Workspace::clientActivated, focusChain, &FocusChain::setActiveClient); connect(VirtualDesktopManager::self(), SIGNAL(countChanged(uint,uint)), focusChain, SLOT(resize(uint,uint))); connect(VirtualDesktopManager::self(), SIGNAL(currentChanged(uint,uint)), focusChain, SLOT(setCurrentDesktop(uint,uint))); connect(options, SIGNAL(separateScreenFocusChanged(bool)), focusChain, SLOT(setSeparateScreenFocus(bool))); focusChain->setSeparateScreenFocus(options->isSeparateScreenFocus()); const uint32_t nullFocusValues[] = {true}; m_nullFocus.reset(new Xcb::Window(QRect(-1, -1, 1, 1), XCB_WINDOW_CLASS_INPUT_ONLY, XCB_CW_OVERRIDE_REDIRECT, nullFocusValues)); m_nullFocus->map(); RootInfo *rootInfo = RootInfo::create(); // create VirtualDesktopManager and perform dependency injection VirtualDesktopManager *vds = VirtualDesktopManager::self(); connect(vds, SIGNAL(desktopsRemoved(uint)), SLOT(moveClientsFromRemovedDesktops())); connect(vds, SIGNAL(countChanged(uint,uint)), SLOT(slotDesktopCountChanged(uint,uint))); connect(vds, SIGNAL(currentChanged(uint,uint)), SLOT(slotCurrentDesktopChanged(uint,uint))); vds->setNavigationWrappingAround(options->isRollOverDesktops()); connect(options, SIGNAL(rollOverDesktopsChanged(bool)), vds, SLOT(setNavigationWrappingAround(bool))); vds->setRootInfo(rootInfo); vds->setConfig(config); // Now we know how many desktops we'll have, thus we initialize the positioning object Placement::create(this); // positioning object needs to be created before the virtual desktops are loaded. vds->load(); vds->updateLayout(); // Extra NETRootInfo instance in Client mode is needed to get the values of the properties NETRootInfo client_info(connection(), NET::ActiveWindow | NET::CurrentDesktop); if (!qApp->isSessionRestored()) m_initialDesktop = client_info.currentDesktop(); if (!VirtualDesktopManager::self()->setCurrent(m_initialDesktop)) VirtualDesktopManager::self()->setCurrent(1); reconfigureTimer.setSingleShot(true); updateToolWindowsTimer.setSingleShot(true); connect(&reconfigureTimer, SIGNAL(timeout()), this, SLOT(slotReconfigure())); connect(&updateToolWindowsTimer, SIGNAL(timeout()), this, SLOT(slotUpdateToolWindows())); // TODO: do we really need to reconfigure everything when fonts change? // maybe just reconfigure the decorations? Move this into libkdecoration? QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KDEPlatformTheme"), QStringLiteral("org.kde.KDEPlatformTheme"), QStringLiteral("refreshFonts"), this, SLOT(reconfigure())); active_client = NULL; rootInfo->setActiveWindow(None); focusToNull(); if (!qApp->isSessionRestored()) ++block_focus; // Because it will be set below { // Begin updates blocker block StackingUpdatesBlocker blocker(this); Xcb::Tree tree(rootWindow()); xcb_window_t *wins = xcb_query_tree_children(tree.data()); QVector windowAttributes(tree->children_len); QVector windowGeometries(tree->children_len); // Request the attributes and geometries of all toplevel windows for (int i = 0; i < tree->children_len; i++) { windowAttributes[i] = Xcb::WindowAttributes(wins[i]); windowGeometries[i] = Xcb::WindowGeometry(wins[i]); } // Get the replies for (int i = 0; i < tree->children_len; i++) { Xcb::WindowAttributes attr(windowAttributes.at(i)); if (attr.isNull()) { continue; } if (attr->override_redirect) { if (attr->map_state == XCB_MAP_STATE_VIEWABLE && attr->_class != XCB_WINDOW_CLASS_INPUT_ONLY) // ### This will request the attributes again createUnmanaged(wins[i]); } else if (attr->map_state != XCB_MAP_STATE_UNMAPPED) { if (Application::wasCrash()) { fixPositionAfterCrash(wins[i], windowGeometries.at(i).data()); } // ### This will request the attributes again createClient(wins[i], true); } } // Propagate clients, will really happen at the end of the updates blocker block updateStackingOrder(true); saveOldScreenSizes(); updateClientArea(); // NETWM spec says we have to set it to (0,0) if we don't support it NETPoint* viewports = new NETPoint[VirtualDesktopManager::self()->count()]; rootInfo->setDesktopViewport(VirtualDesktopManager::self()->count(), *viewports); delete[] viewports; QRect geom; for (int i = 0; i < screens->count(); i++) { geom |= screens->geometry(i); } NETSize desktop_geometry; desktop_geometry.width = geom.width(); desktop_geometry.height = geom.height(); rootInfo->setDesktopGeometry(desktop_geometry); setShowingDesktop(false); } // End updates blocker block AbstractClient* new_active_client = nullptr; if (!qApp->isSessionRestored()) { --block_focus; new_active_client = findClient(Predicate::WindowMatch, client_info.activeWindow()); } if (new_active_client == NULL && activeClient() == NULL && should_get_focus.count() == 0) { // No client activated in manage() if (new_active_client == NULL) new_active_client = topClientOnDesktop(VirtualDesktopManager::self()->current(), -1); if (new_active_client == NULL && !desktops.isEmpty()) new_active_client = findDesktop(true, VirtualDesktopManager::self()->current()); } if (new_active_client != NULL) activateClient(new_active_client); Scripting::create(this); if (auto w = waylandServer()) { connect(w, &WaylandServer::shellClientAdded, this, [this] (ShellClient *c) { c->updateDecoration(false); updateClientLayer(c); if (!c->isInternal()) { QRect area = clientArea(PlacementArea, Screens::self()->current(), c->desktop()); bool placementDone = false; if (c->isInitialPositionSet()) { placementDone = true; } if (c->isFullScreen()) { placementDone = true; } if (!placementDone) { Placement::self()->place(c, area); } m_allClients.append(c); if (!unconstrained_stacking_order.contains(c)) unconstrained_stacking_order.append(c); // Raise if it hasn't got any stacking position yet if (!stacking_order.contains(c)) // It'll be updated later, and updateToolWindows() requires stacking_order.append(c); // c to be in stacking_order } x_stacking_dirty = true; updateStackingOrder(true); updateClientArea(); if (c->wantsInput()) { activateClient(c); } } ); connect(w, &WaylandServer::shellClientRemoved, this, [this] (ShellClient *c) { m_allClients.removeAll(c); if (c == delayfocus_client) { cancelDelayFocus(); } clientHidden(c); emit clientRemoved(c); x_stacking_dirty = true; updateStackingOrder(true); updateClientArea(); } ); } // SELI TODO: This won't work with unreasonable focus policies, // and maybe in rare cases also if the selected client doesn't // want focus workspaceInit = false; // broadcast that Workspace is ready, but first process all events. QMetaObject::invokeMethod(this, "workspaceInitialized", Qt::QueuedConnection); // TODO: ungrabXServer() } Workspace::~Workspace() { blockStackingUpdates(true); // TODO: grabXServer(); // Use stacking_order, so that kwin --replace keeps stacking order const ToplevelList stack = stacking_order; // "mutex" the stackingorder, since anything trying to access it from now on will find // many dangeling pointers and crash stacking_order.clear(); for (ToplevelList::const_iterator it = stack.constBegin(), end = stack.constEnd(); it != end; ++it) { Client *c = qobject_cast(const_cast(*it)); if (!c) { continue; } // Only release the window c->releaseWindow(true); // No removeClient() is called, it does more than just removing. // However, remove from some lists to e.g. prevent performTransiencyCheck() // from crashing. clients.removeAll(c); m_allClients.removeAll(c); desktops.removeAll(c); } Client::cleanupX11(); for (UnmanagedList::iterator it = unmanaged.begin(), end = unmanaged.end(); it != end; ++it) (*it)->release(ReleaseReason::KWinShutsDown); xcb_delete_property(connection(), rootWindow(), atoms->kwin_running); delete RuleBook::self(); kwinApp()->config()->sync(); RootInfo::destroy(); delete startup; delete Placement::self(); delete client_keys_dialog; foreach (SessionInfo * s, session) delete s; // TODO: ungrabXServer(); + if (kwinApp()->operationMode() == Application::OperationModeX11) { + XRenderUtils::cleanup(); + } Xcb::Extensions::destroy(); _self = 0; } Client* Workspace::createClient(xcb_window_t w, bool is_mapped) { StackingUpdatesBlocker blocker(this); Client* c = new Client(); connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint())); connect(c, &Client::activeChanged, m_compositor, static_cast(&Compositor::checkUnredirect)); connect(c, SIGNAL(fullScreenChanged()), m_compositor, SLOT(checkUnredirect())); connect(c, SIGNAL(geometryChanged()), m_compositor, SLOT(checkUnredirect())); connect(c, SIGNAL(geometryShapeChanged(KWin::Toplevel*,QRect)), m_compositor, SLOT(checkUnredirect())); connect(c, SIGNAL(blockingCompositingChanged(KWin::Client*)), m_compositor, SLOT(updateCompositeBlocking(KWin::Client*))); connect(c, SIGNAL(clientFullScreenSet(KWin::Client*,bool,bool)), ScreenEdges::self(), SIGNAL(checkBlocking())); connect(c, &Client::desktopPresenceChanged, this, &Workspace::desktopPresenceChanged); if (!c->manage(w, is_mapped)) { Client::deleteClient(c); return NULL; } addClient(c); return c; } Unmanaged* Workspace::createUnmanaged(xcb_window_t w) { if (m_compositor && m_compositor->checkForOverlayWindow(w)) return NULL; Unmanaged* c = new Unmanaged(); if (!c->track(w)) { Unmanaged::deleteUnmanaged(c); return NULL; } connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint())); addUnmanaged(c); emit unmanagedAdded(c); return c; } void Workspace::addClient(Client* c) { Group* grp = findGroup(c->window()); emit clientAdded(c); if (grp != NULL) grp->gotLeader(c); if (c->isDesktop()) { desktops.append(c); if (active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop()) requestFocus(c); // TODO: Make sure desktop is active after startup if there's no other window active } else { FocusChain::self()->update(c, FocusChain::Update); clients.append(c); m_allClients.append(c); } if (!unconstrained_stacking_order.contains(c)) unconstrained_stacking_order.append(c); // Raise if it hasn't got any stacking position yet if (!stacking_order.contains(c)) // It'll be updated later, and updateToolWindows() requires stacking_order.append(c); // c to be in stacking_order x_stacking_dirty = true; updateClientArea(); // This cannot be in manage(), because the client got added only now updateClientLayer(c); if (c->isDesktop()) { raiseClient(c); // If there's no active client, make this desktop the active one if (activeClient() == NULL && should_get_focus.count() == 0) activateClient(findDesktop(true, VirtualDesktopManager::self()->current())); } c->checkActiveModal(); checkTransients(c->window()); // SELI TODO: Does this really belong here? updateStackingOrder(true); // Propagate new client if (c->isUtility() || c->isMenu() || c->isToolbar()) updateToolWindows(true); checkNonExistentClients(); #ifdef KWIN_BUILD_TABBOX if (TabBox::TabBox::self()->isDisplayed()) TabBox::TabBox::self()->reset(true); #endif } void Workspace::addUnmanaged(Unmanaged* c) { unmanaged.append(c); x_stacking_dirty = true; } /** * Destroys the client \a c */ void Workspace::removeClient(Client* c) { emit clientRemoved(c); if (c == active_popup_client) closeActivePopup(); if (m_userActionsMenu->isMenuClient(c)) { m_userActionsMenu->close(); } c->untab(QRect(), true); if (client_keys_client == c) setupWindowShortcutDone(false); if (!c->shortcut().isEmpty()) { c->setShortcut(QString()); // Remove from client_keys clientShortcutUpdated(c); // Needed, since this is otherwise delayed by setShortcut() and wouldn't run } #ifdef KWIN_BUILD_TABBOX TabBox::TabBox *tabBox = TabBox::TabBox::self(); if (tabBox->isDisplayed() && tabBox->currentClient() == c) tabBox->nextPrev(true); #endif Q_ASSERT(clients.contains(c) || desktops.contains(c)); // TODO: if marked client is removed, notify the marked list clients.removeAll(c); m_allClients.removeAll(c); desktops.removeAll(c); x_stacking_dirty = true; attention_chain.removeAll(c); Group* group = findGroup(c->window()); if (group != NULL) group->lostLeader(); if (c == most_recently_raised) most_recently_raised = 0; should_get_focus.removeAll(c); Q_ASSERT(c != active_client); if (c == last_active_client) last_active_client = 0; if (c == delayfocus_client) cancelDelayFocus(); updateStackingOrder(true); #ifdef KWIN_BUILD_TABBOX if (tabBox->isDisplayed()) tabBox->reset(true); #endif updateClientArea(); } void Workspace::removeUnmanaged(Unmanaged* c) { assert(unmanaged.contains(c)); unmanaged.removeAll(c); emit unmanagedRemoved(c); x_stacking_dirty = true; } void Workspace::addDeleted(Deleted* c, Toplevel *orig) { assert(!deleted.contains(c)); deleted.append(c); const int unconstraintedIndex = unconstrained_stacking_order.indexOf(orig); if (unconstraintedIndex != -1) { unconstrained_stacking_order.replace(unconstraintedIndex, c); } else { unconstrained_stacking_order.append(c); } const int index = stacking_order.indexOf(orig); if (index != -1) { stacking_order.replace(index, c); } else { stacking_order.append(c); } x_stacking_dirty = true; connect(c, SIGNAL(needsRepaint()), m_compositor, SLOT(scheduleRepaint())); } void Workspace::removeDeleted(Deleted* c) { assert(deleted.contains(c)); emit deletedRemoved(c); deleted.removeAll(c); unconstrained_stacking_order.removeAll(c); stacking_order.removeAll(c); x_stacking_dirty = true; if (c->wasClient() && m_compositor) { m_compositor->updateCompositeBlocking(); } } void Workspace::updateToolWindows(bool also_hide) { // TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?) if (!options->isHideUtilityWindowsForInactive()) { for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) if (!(*it)->tabGroup() || (*it)->tabGroup()->current() == *it) (*it)->hideClient(false); return; } const Group* group = NULL; const Client* client = dynamic_cast(active_client); // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow // will be shown; if a group transient is group, all tools in the group will be shown while (client != NULL) { if (!client->isTransient()) break; if (client->groupTransient()) { group = client->group(); break; } client = dynamic_cast(client->transientFor()); } // Use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0, // I.e. if it's not up to date // SELI TODO: But maybe it should - what if a new client has been added that's not in stacking order yet? ClientList to_show, to_hide; for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { Client *c = qobject_cast(*it); if (!c) { continue; } if (c->isUtility() || c->isMenu() || c->isToolbar()) { bool show = true; if (!c->isTransient()) { if (c->group()->members().count() == 1) // Has its own group, keep always visible show = true; else if (client != NULL && c->group() == client->group()) show = true; else show = false; } else { if (group != NULL && c->group() == group) show = true; else if (client != NULL && client->hasTransient(c, true)) show = true; else show = false; } if (!show && also_hide) { const auto mainclients = c->mainClients(); // Don't hide utility windows which are standalone(?) or // have e.g. kicker as mainwindow if (mainclients.isEmpty()) show = true; for (auto it2 = mainclients.constBegin(); it2 != mainclients.constEnd(); ++it2) { if ((*it2)->isSpecialWindow()) show = true; } if (!show) to_hide.append(c); } if (show) to_show.append(c); } } // First show new ones, then hide for (int i = to_show.size() - 1; i >= 0; --i) // From topmost // TODO: Since this is in stacking order, the order of taskbar entries changes :( to_show.at(i)->hideClient(false); if (also_hide) { for (ClientList::ConstIterator it = to_hide.constBegin(); it != to_hide.constEnd(); ++it) // From bottommost (*it)->hideClient(true); updateToolWindowsTimer.stop(); } else // setActiveClient() is after called with NULL client, quickly followed // by setting a new client, which would result in flickering resetUpdateToolWindowsTimer(); } void Workspace::resetUpdateToolWindowsTimer() { updateToolWindowsTimer.start(200); } void Workspace::slotUpdateToolWindows() { updateToolWindows(true); } void Workspace::slotReloadConfig() { reconfigure(); } void Workspace::reconfigure() { reconfigureTimer.start(200); } /** * This D-Bus call is used by the compositing kcm. Since the reconfigure() * D-Bus call delays the actual reconfiguring, it is not possible to immediately * call compositingActive(). Therefore the kcm will instead call this to ensure * the reconfiguring has already happened. */ bool Workspace::waitForCompositingSetup() { if (reconfigureTimer.isActive()) { reconfigureTimer.stop(); slotReconfigure(); } if (m_compositor) { return m_compositor->isActive(); } return false; } /** * Reread settings */ void Workspace::slotReconfigure() { qCDebug(KWIN_CORE) << "Workspace::slotReconfigure()"; reconfigureTimer.stop(); bool borderlessMaximizedWindows = options->borderlessMaximizedWindows(); kwinApp()->config()->reparseConfiguration(); options->updateSettings(); emit configChanged(); m_userActionsMenu->discard(); updateToolWindows(true); RuleBook::self()->load(); for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { (*it)->setupWindowRules(true); (*it)->applyWindowRules(); RuleBook::self()->discardUsed(*it, false); } if (borderlessMaximizedWindows != options->borderlessMaximizedWindows() && !options->borderlessMaximizedWindows()) { // in case borderless maximized windows option changed and new option // is to have borders, we need to unset the borders for all maximized windows for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { if ((*it)->maximizeMode() == MaximizeFull) (*it)->checkNoBorder(); } } } /** * During virt. desktop switching, desktop areas covered by windows that are * going to be hidden are first obscured by new windows with no background * ( i.e. transparent ) placed right below the windows. These invisible windows * are removed after the switch is complete. * Reduces desktop ( wallpaper ) repaints during desktop switching */ class ObscuringWindows { public: ~ObscuringWindows(); void create(Client* c); private: QList obscuring_windows; static QList* cached; static unsigned int max_cache_size; }; QList* ObscuringWindows::cached = nullptr; unsigned int ObscuringWindows::max_cache_size = 0; void ObscuringWindows::create(Client* c) { if (!cached) cached = new QList; Xcb::Window obs_win(XCB_WINDOW_NONE, false); if (cached->count() > 0) { obs_win.reset(cached->first(), false); cached->removeAll(obs_win); obs_win.setGeometry(c->geometry()); } else { uint32_t values[] = {XCB_PIXMAP_NONE, true}; obs_win.create(c->geometry(), XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CW_BACK_PIXMAP | XCB_CW_OVERRIDE_REDIRECT, values); } uint32_t values[] = {c->frameId(), XCB_STACK_MODE_BELOW}; xcb_configure_window(connection(), obs_win, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values); obs_win.map(); obscuring_windows.append(obs_win); } ObscuringWindows::~ObscuringWindows() { max_cache_size = qMax(int(max_cache_size), obscuring_windows.count() + 4) - 1; for (auto it = obscuring_windows.constBegin(); it != obscuring_windows.constEnd(); ++it) { xcb_unmap_window(connection(), *it); if (cached->count() < int(max_cache_size)) cached->prepend(*it); else xcb_destroy_window(connection(), *it); } } void Workspace::slotCurrentDesktopChanged(uint oldDesktop, uint newDesktop) { closeActivePopup(); ++block_focus; StackingUpdatesBlocker blocker(this); updateClientVisibilityOnDesktopChange(oldDesktop, newDesktop); // Restore the focus on this desktop --block_focus; activateClientOnNewDesktop(newDesktop); emit currentDesktopChanged(oldDesktop, movingClient); } void Workspace::updateClientVisibilityOnDesktopChange(uint oldDesktop, uint newDesktop) { ObscuringWindows obs_wins; for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { Client *c = qobject_cast(*it); if (!c) { continue; } if (!c->isOnDesktop(newDesktop) && c != movingClient && c->isOnCurrentActivity()) { if (c->isShown(true) && c->isOnDesktop(oldDesktop) && !compositing()) obs_wins.create(c); (c)->updateVisibility(); } } // Now propagate the change, after hiding, before showing rootInfo()->setCurrentDesktop(VirtualDesktopManager::self()->current()); if (movingClient && !movingClient->isOnDesktop(newDesktop)) { movingClient->setDesktop(newDesktop); } for (int i = stacking_order.size() - 1; i >= 0 ; --i) { Client *c = qobject_cast(stacking_order.at(i)); if (!c) { continue; } if (c->isOnDesktop(newDesktop) && c->isOnCurrentActivity()) c->updateVisibility(); } if (showingDesktop()) // Do this only after desktop change to avoid flicker setShowingDesktop(false); } void Workspace::activateClientOnNewDesktop(uint desktop) { AbstractClient* c = NULL; if (options->focusPolicyIsReasonable()) { c = findClientToActivateOnDesktop(desktop); } // If "unreasonable focus policy" and active_client is on_all_desktops and // under mouse (Hence == old_active_client), conserve focus. // (Thanks to Volker Schatz ) else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop()) c = active_client; if (c == NULL && !desktops.isEmpty()) c = findDesktop(true, desktop); if (c != active_client) setActiveClient(NULL); if (c) requestFocus(c); else if (!desktops.isEmpty()) requestFocus(findDesktop(true, desktop)); else focusToNull(); } AbstractClient *Workspace::findClientToActivateOnDesktop(uint desktop) { if (movingClient != NULL && active_client == movingClient && FocusChain::self()->contains(active_client, desktop) && active_client->isShown(true) && active_client->isOnCurrentDesktop()) { // A requestFocus call will fail, as the client is already active return active_client; } // from actiavtion.cpp if (options->isNextFocusPrefersMouse()) { ToplevelList::const_iterator it = stackingOrder().constEnd(); while (it != stackingOrder().constBegin()) { Client *client = qobject_cast(*(--it)); if (!client) { continue; } if (!(client->isShown(false) && client->isOnDesktop(desktop) && client->isOnCurrentActivity() && client->isOnActiveScreen())) continue; if (client->geometry().contains(Cursor::pos())) { if (!client->isDesktop()) return client; break; // unconditional break - we do not pass the focus to some client below an unusable one } } } return FocusChain::self()->getForActivation(desktop); } /** * Updates the current activity when it changes * do *not* call this directly; it does not set the activity. * * Shows/Hides windows according to the stacking order */ void Workspace::updateCurrentActivity(const QString &new_activity) { #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return; } //closeActivePopup(); ++block_focus; // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date StackingUpdatesBlocker blocker(this); // Optimized Desktop switching: unmapping done from back to front // mapping done from front to back => less exposure events //Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop)); ObscuringWindows obs_wins; const QString &old_activity = Activities::self()->previous(); for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { Client *c = qobject_cast(*it); if (!c) { continue; } if (!c->isOnActivity(new_activity) && c != movingClient && c->isOnCurrentDesktop()) { if (c->isShown(true) && c->isOnActivity(old_activity) && !compositing()) obs_wins.create(c); c->updateVisibility(); } } // Now propagate the change, after hiding, before showing //rootInfo->setCurrentDesktop( currentDesktop() ); /* TODO someday enable dragging windows to other activities if ( movingClient && !movingClient->isOnDesktop( new_desktop )) { movingClient->setDesktop( new_desktop ); */ for (int i = stacking_order.size() - 1; i >= 0 ; --i) { Client *c = qobject_cast(stacking_order.at(i)); if (!c) { continue; } if (c->isOnActivity(new_activity)) c->updateVisibility(); } //FIXME not sure if I should do this either if (showingDesktop()) // Do this only after desktop change to avoid flicker setShowingDesktop(false); // Restore the focus on this desktop --block_focus; AbstractClient* c = 0; //FIXME below here is a lot of focuschain stuff, probably all wrong now if (options->focusPolicyIsReasonable()) { // Search in focus chain c = FocusChain::self()->getForActivation(VirtualDesktopManager::self()->current()); } // If "unreasonable focus policy" and active_client is on_all_desktops and // under mouse (Hence == old_active_client), conserve focus. // (Thanks to Volker Schatz ) else if (active_client && active_client->isShown(true) && active_client->isOnCurrentDesktop() && active_client->isOnCurrentActivity()) c = active_client; if (c == NULL && !desktops.isEmpty()) c = findDesktop(true, VirtualDesktopManager::self()->current()); if (c != active_client) setActiveClient(NULL); if (c) requestFocus(c); else if (!desktops.isEmpty()) requestFocus(findDesktop(true, VirtualDesktopManager::self()->current())); else focusToNull(); // Not for the very first time, only if something changed and there are more than 1 desktops //if ( effects != NULL && old_desktop != 0 && old_desktop != new_desktop ) // static_cast( effects )->desktopChanged( old_desktop ); if (compositing() && m_compositor) m_compositor->addRepaintFull(); #else Q_UNUSED(new_activity) #endif } void Workspace::moveClientsFromRemovedDesktops() { for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) { if (!(*it)->isOnAllDesktops() && (*it)->desktop() > static_cast(VirtualDesktopManager::self()->count())) sendClientToDesktop(*it, VirtualDesktopManager::self()->count(), true); } } void Workspace::slotDesktopCountChanged(uint previousCount, uint newCount) { Q_UNUSED(previousCount) Placement::self()->reinitCascading(0); resetClientAreas(newCount); } void Workspace::resetClientAreas(uint desktopCount) { // Make it +1, so that it can be accessed as [1..numberofdesktops] workarea.clear(); workarea.resize(desktopCount + 1); restrictedmovearea.clear(); restrictedmovearea.resize(desktopCount + 1); screenarea.clear(); updateClientArea(true); } void Workspace::selectWmInputEventMask() { uint32_t presentMask = 0; Xcb::WindowAttributes attr(rootWindow()); if (!attr.isNull()) { presentMask = attr->your_event_mask; } Xcb::selectInput(rootWindow(), presentMask | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_FOCUS_CHANGE | // For NotifyDetailNone XCB_EVENT_MASK_EXPOSURE ); } /** * Sends client \a c to desktop \a desk. * * Takes care of transients as well. */ void Workspace::sendClientToDesktop(AbstractClient* c, int desk, bool dont_activate) { if ((desk < 1 && desk != NET::OnAllDesktops) || desk > static_cast(VirtualDesktopManager::self()->count())) return; int old_desktop = c->desktop(); bool was_on_desktop = c->isOnDesktop(desk) || c->isOnAllDesktops(); c->setDesktop(desk); if (c->desktop() != desk) // No change or desktop forced return; desk = c->desktop(); // Client did range checking if (c->isOnDesktop(VirtualDesktopManager::self()->current())) { if (c->wantsTabFocus() && options->focusPolicyIsReasonable() && !was_on_desktop && // for stickyness changes !dont_activate) requestFocus(c); else restackClientUnderActive(c); } else raiseClient(c); c->checkWorkspacePosition( QRect(), old_desktop ); if (Client *client = dynamic_cast(c)) { // TODO: adjust transients for non-X11 auto transients_stacking_order = ensureStackingOrder(client->transients()); for (auto it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) sendClientToDesktop(*it, desk, dont_activate); } updateClientArea(); } /** * checks whether the X Window with the input focus is on our X11 screen * if the window cannot be determined or inspected, resturn depends on whether there's actually * more than one screen * * this is NOT in any way related to XRandR multiscreen * */ extern bool is_multihead; // main.cpp bool Workspace::isOnCurrentHead() { if (!is_multihead) { return true; } Xcb::CurrentInput currentInput; if (currentInput.window() == XCB_WINDOW_NONE) { return !is_multihead; } Xcb::WindowGeometry geometry(currentInput.window()); if (geometry.isNull()) { // should not happen return !is_multihead; } return rootWindow() == geometry->root; } void Workspace::sendClientToScreen(AbstractClient* c, int screen) { c->sendToScreen(screen); } void Workspace::sendPingToWindow(xcb_window_t window, xcb_timestamp_t timestamp) { rootInfo()->sendPing(window, timestamp); } /** * Delayed focus functions */ void Workspace::delayFocus() { requestFocus(delayfocus_client); cancelDelayFocus(); } void Workspace::requestDelayFocus(AbstractClient* c) { delayfocus_client = c; delete delayFocusTimer; delayFocusTimer = new QTimer(this); connect(delayFocusTimer, SIGNAL(timeout()), this, SLOT(delayFocus())); delayFocusTimer->setSingleShot(true); delayFocusTimer->start(options->delayFocusInterval()); } void Workspace::cancelDelayFocus() { delete delayFocusTimer; delayFocusTimer = 0; } bool Workspace::checkStartupNotification(xcb_window_t w, KStartupInfoId &id, KStartupInfoData &data) { return startup->checkStartup(w, id, data) == KStartupInfo::Match; } /** * Puts the focus on a dummy window * Just using XSetInputFocus() with None would block keyboard input */ void Workspace::focusToNull() { m_nullFocus->focus(); } void Workspace::setShowingDesktop(bool showing) { const bool changed = showing != showing_desktop; rootInfo()->setShowingDesktop(showing); showing_desktop = showing; AbstractClient *topDesk = nullptr; { // for the blocker RAII StackingUpdatesBlocker blocker(this); // updateLayer & lowerClient would invalidate stacking_order for (int i = stacking_order.count() - 1; i > -1; --i) { AbstractClient *c = qobject_cast(stacking_order.at(i)); if (c && c->isOnCurrentDesktop()) { if (c->isDock()) { c->updateLayer(); } else if (c->isDesktop() && c->isShown(true)) { c->updateLayer(); lowerClient(c); if (!topDesk) topDesk = c; if (Client *client = qobject_cast(c)) { foreach (Client *cm, client->group()->members()) { cm->updateLayer(); } } } } } } // ~StackingUpdatesBlocker if (showing_desktop && topDesk) requestFocus(topDesk); if (changed) emit showingDesktopChanged(showing); } void Workspace::disableGlobalShortcutsForClient(bool disable) { if (global_shortcuts_disabled_for_client == disable) return; QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kglobalaccel"), QStringLiteral("/kglobalaccel"), QStringLiteral("org.kde.KGlobalAccel"), QStringLiteral("blockGlobalShortcuts")); message.setArguments(QList() << disable); QDBusConnection::sessionBus().asyncCall(message); global_shortcuts_disabled_for_client = disable; // Update also Alt+LMB actions etc. for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) (*it)->updateMouseGrab(); } QString Workspace::supportInformation() const { QString support; const QString yes = QStringLiteral("yes\n"); const QString no = QStringLiteral("no\n"); support.append(ki18nc("Introductory text shown in the support information.", "KWin Support Information:\n" "The following information should be used when requesting support on e.g. http://forum.kde.org.\n" "It provides information about the currently running instance, which options are used,\n" "what OpenGL driver and which effects are running.\n" "Please post the information provided underneath this introductory text to a paste bin service\n" "like http://paste.kde.org instead of pasting into support threads.\n").toString()); support.append(QStringLiteral("\n==========================\n\n")); // all following strings are intended for support. They need to be pasted to e.g forums.kde.org // it is expected that the support will happen in English language or that the people providing // help understand English. Because of that all texts are not translated support.append(QStringLiteral("Version\n")); support.append(QStringLiteral("=======\n")); support.append(QStringLiteral("KWin version: ")); support.append(QStringLiteral(KWIN_VERSION_STRING)); support.append(QStringLiteral("\n")); support.append(QStringLiteral("Qt Version: ")); support.append(QString::fromUtf8(qVersion())); support.append(QStringLiteral("\n")); support.append(QStringLiteral("Qt compile version: %1\n").arg(QStringLiteral(QT_VERSION_STR))); support.append(QStringLiteral("XCB compile version: %1\n\n").arg(QStringLiteral(XCB_VERSION_STRING))); support.append(QStringLiteral("Operation Mode: ")); switch (kwinApp()->operationMode()) { case Application::OperationModeX11: support.append(QStringLiteral("X11 only")); break; case Application::OperationModeWaylandAndX11: support.append(QStringLiteral("Wayland and X11")); break; case Application::OperationModeXwayland: support.append(QStringLiteral("Xwayland")); break; } support.append(QStringLiteral("\n\n")); support.append(QStringLiteral("Build Options\n")); support.append(QStringLiteral("=============\n")); support.append(QStringLiteral("KWIN_BUILD_DECORATIONS: ")); #ifdef KWIN_BUILD_DECORATIONS support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("KWIN_BUILD_TABBOX: ")); #ifdef KWIN_BUILD_TABBOX support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("KWIN_BUILD_ACTIVITIES: ")); #ifdef KWIN_BUILD_ACTIVITIES support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("HAVE_INPUT: ")); #if HAVE_INPUT support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("HAVE_DRM: ")); #if HAVE_DRM support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("HAVE_GBM: ")); #if HAVE_GBM support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("HAVE_X11_XCB: ")); #if HAVE_X11_XCB support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("HAVE_EPOXY_GLX: ")); #if HAVE_EPOXY_GLX support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("HAVE_WAYLAND_EGL: ")); #if HAVE_WAYLAND_EGL support.append(yes); #else support.append(no); #endif support.append(QStringLiteral("\n")); support.append(QStringLiteral("X11\n")); support.append(QStringLiteral("===\n")); auto x11setup = xcb_get_setup(connection()); support.append(QStringLiteral("Vendor: %1\n").arg(QString::fromUtf8(QByteArray::fromRawData(xcb_setup_vendor(x11setup), xcb_setup_vendor_length(x11setup))))); support.append(QStringLiteral("Vendor Release: %1\n").arg(x11setup->release_number)); support.append(QStringLiteral("Protocol Version/Revision: %1/%2\n").arg(x11setup->protocol_major_version).arg(x11setup->protocol_minor_version)); const auto extensions = Xcb::Extensions::self()->extensions(); for (const auto &e : extensions) { support.append(QStringLiteral("%1: %2; Version: 0x%3\n").arg(QString::fromUtf8(e.name)) .arg(e.present ? yes.trimmed() : no.trimmed()) .arg(QString::number(e.version, 16))); } support.append(QStringLiteral("\n")); if (auto bridge = Decoration::DecorationBridge::self()) { support.append(QStringLiteral("Decoration\n")); support.append(QStringLiteral("==========\n")); support.append(bridge->supportInformation()); support.append(QStringLiteral("\n")); } support.append(QStringLiteral("Options\n")); support.append(QStringLiteral("=======\n")); const QMetaObject *metaOptions = options->metaObject(); auto printProperty = [] (const QVariant &variant) { if (variant.type() == QVariant::Size) { const QSize &s = variant.toSize(); return QStringLiteral("%1x%2").arg(QString::number(s.width())).arg(QString::number(s.height())); } if (QLatin1String(variant.typeName()) == QLatin1String("KWin::OpenGLPlatformInterface") || QLatin1String(variant.typeName()) == QLatin1String("KWin::Options::WindowOperation")) { return QString::number(variant.toInt()); } return variant.toString(); }; for (int i=0; ipropertyCount(); ++i) { const QMetaProperty property = metaOptions->property(i); if (QLatin1String(property.name()) == QLatin1String("objectName")) { continue; } support.append(QStringLiteral("%1: %2\n").arg(property.name()).arg(printProperty(options->property(property.name())))); } support.append(QStringLiteral("\nScreen Edges\n")); support.append(QStringLiteral( "============\n")); const QMetaObject *metaScreenEdges = ScreenEdges::self()->metaObject(); for (int i=0; ipropertyCount(); ++i) { const QMetaProperty property = metaScreenEdges->property(i); if (QLatin1String(property.name()) == QLatin1String("objectName")) { continue; } support.append(QStringLiteral("%1: %2\n").arg(property.name()).arg(printProperty(ScreenEdges::self()->property(property.name())))); } support.append(QStringLiteral("\nScreens\n")); support.append(QStringLiteral( "=======\n")); support.append(QStringLiteral("Multi-Head: ")); if (is_multihead) { support.append(QStringLiteral("yes\n")); support.append(QStringLiteral("Head: %1\n").arg(screen_number)); } else { support.append(QStringLiteral("no\n")); } support.append(QStringLiteral("Active screen follows mouse: ")); if (screens()->isCurrentFollowsMouse()) support.append(QStringLiteral(" yes\n")); else support.append(QStringLiteral(" no\n")); support.append(QStringLiteral("Number of Screens: %1\n\n").arg(screens()->count())); for (int i=0; icount(); ++i) { const QRect geo = screens()->geometry(i); support.append(QStringLiteral("Screen %1:\n").arg(i)); support.append(QStringLiteral("---------\n").arg(i)); support.append(QStringLiteral("Name: %1\n").arg(screens()->name(i))); support.append(QStringLiteral("Geometry: %1,%2,%3x%4\n") .arg(geo.x()) .arg(geo.y()) .arg(geo.width()) .arg(geo.height())); support.append(QStringLiteral("Refresh Rate: %1\n\n").arg(screens()->refreshRate(i))); } support.append(QStringLiteral("\nCompositing\n")); support.append(QStringLiteral( "===========\n")); if (effects) { support.append(QStringLiteral("Compositing is active\n")); switch (effects->compositingType()) { case OpenGL2Compositing: case OpenGLCompositing: { GLPlatform *platform = GLPlatform::instance(); if (platform->isGLES()) { support.append(QStringLiteral("Compositing Type: OpenGL ES 2.0\n")); } else { support.append(QStringLiteral("Compositing Type: OpenGL\n")); } support.append(QStringLiteral("OpenGL vendor string: ") + QString::fromUtf8(platform->glVendorString()) + QStringLiteral("\n")); support.append(QStringLiteral("OpenGL renderer string: ") + QString::fromUtf8(platform->glRendererString()) + QStringLiteral("\n")); support.append(QStringLiteral("OpenGL version string: ") + QString::fromUtf8(platform->glVersionString()) + QStringLiteral("\n")); support.append(QStringLiteral("OpenGL platform interface: ")); switch (platform->platformInterface()) { case GlxPlatformInterface: support.append(QStringLiteral("GLX")); break; case EglPlatformInterface: support.append(QStringLiteral("EGL")); break; default: support.append(QStringLiteral("UNKNOWN")); } support.append(QStringLiteral("\n")); if (platform->supports(LimitedGLSL) || platform->supports(GLSL)) support.append(QStringLiteral("OpenGL shading language version string: ") + QString::fromUtf8(platform->glShadingLanguageVersionString()) + QStringLiteral("\n")); support.append(QStringLiteral("Driver: ") + GLPlatform::driverToString(platform->driver()) + QStringLiteral("\n")); if (!platform->isMesaDriver()) support.append(QStringLiteral("Driver version: ") + GLPlatform::versionToString(platform->driverVersion()) + QStringLiteral("\n")); support.append(QStringLiteral("GPU class: ") + GLPlatform::chipClassToString(platform->chipClass()) + QStringLiteral("\n")); support.append(QStringLiteral("OpenGL version: ") + GLPlatform::versionToString(platform->glVersion()) + QStringLiteral("\n")); if (platform->supports(LimitedGLSL) || platform->supports(GLSL)) support.append(QStringLiteral("GLSL version: ") + GLPlatform::versionToString(platform->glslVersion()) + QStringLiteral("\n")); if (platform->isMesaDriver()) support.append(QStringLiteral("Mesa version: ") + GLPlatform::versionToString(platform->mesaVersion()) + QStringLiteral("\n")); if (platform->serverVersion() > 0) support.append(QStringLiteral("X server version: ") + GLPlatform::versionToString(platform->serverVersion()) + QStringLiteral("\n")); if (platform->kernelVersion() > 0) support.append(QStringLiteral("Linux kernel version: ") + GLPlatform::versionToString(platform->kernelVersion()) + QStringLiteral("\n")); support.append(QStringLiteral("Direct rendering: ")); support.append(QStringLiteral("Requires strict binding: ")); if (!platform->isLooseBinding()) { support.append(QStringLiteral("yes\n")); } else { support.append(QStringLiteral("no\n")); } support.append(QStringLiteral("GLSL shaders: ")); if (platform->supports(GLSL)) { if (platform->supports(LimitedGLSL)) { support.append(QStringLiteral(" limited\n")); } else { support.append(QStringLiteral(" yes\n")); } } else { support.append(QStringLiteral(" no\n")); } support.append(QStringLiteral("Texture NPOT support: ")); if (platform->supports(TextureNPOT)) { if (platform->supports(LimitedNPOT)) { support.append(QStringLiteral(" limited\n")); } else { support.append(QStringLiteral(" yes\n")); } } else { support.append(QStringLiteral(" no\n")); } support.append(QStringLiteral("Virtual Machine: ")); if (platform->isVirtualMachine()) { support.append(QStringLiteral(" yes\n")); } else { support.append(QStringLiteral(" no\n")); } support.append(QStringLiteral("OpenGL 2 Shaders are used\n")); support.append(QStringLiteral("Painting blocks for vertical retrace: ")); if (m_compositor->scene()->blocksForRetrace()) support.append(QStringLiteral(" yes\n")); else support.append(QStringLiteral(" no\n")); break; } case XRenderCompositing: support.append(QStringLiteral("Compositing Type: XRender\n")); break; case QPainterCompositing: support.append("Compositing Type: QPainter\n"); break; case NoCompositing: default: support.append(QStringLiteral("Something is really broken, neither OpenGL nor XRender is used")); } support.append(QStringLiteral("\nLoaded Effects:\n")); support.append(QStringLiteral( "---------------\n")); foreach (const QString &effect, static_cast(effects)->loadedEffects()) { support.append(effect + QStringLiteral("\n")); } support.append(QStringLiteral("\nCurrently Active Effects:\n")); support.append(QStringLiteral( "-------------------------\n")); foreach (const QString &effect, static_cast(effects)->activeEffects()) { support.append(effect + QStringLiteral("\n")); } support.append(QStringLiteral("\nEffect Settings:\n")); support.append(QStringLiteral( "----------------\n")); foreach (const QString &effect, static_cast(effects)->loadedEffects()) { support.append(static_cast(effects)->supportInformation(effect)); support.append(QStringLiteral("\n")); } } else { support.append(QStringLiteral("Compositing is not active\n")); } return support; } void Workspace::slotToggleCompositing() { if (m_compositor) { m_compositor->slotToggleCompositing(); } } Client *Workspace::findClient(std::function func) const { if (Client *ret = Toplevel::findInList(clients, func)) { return ret; } if (Client *ret = Toplevel::findInList(desktops, func)) { return ret; } return nullptr; } AbstractClient *Workspace::findAbstractClient(std::function func) const { if (AbstractClient *ret = Toplevel::findInList(m_allClients, func)) { return ret; } if (Client *ret = Toplevel::findInList(desktops, func)) { return ret; } return nullptr; } Unmanaged *Workspace::findUnmanaged(std::function func) const { return Toplevel::findInList(unmanaged, func); } Unmanaged *Workspace::findUnmanaged(xcb_window_t w) const { return findUnmanaged([w](const Unmanaged *u) { return u->window() == w; }); } Client *Workspace::findClient(Predicate predicate, xcb_window_t w) const { switch (predicate) { case Predicate::WindowMatch: return findClient([w](const Client *c) { return c->window() == w; }); case Predicate::WrapperIdMatch: return findClient([w](const Client *c) { return c->wrapperId() == w; }); case Predicate::FrameIdMatch: return findClient([w](const Client *c) { return c->frameId() == w; }); case Predicate::InputIdMatch: return findClient([w](const Client *c) { return c->inputId() == w; }); } return nullptr; } Toplevel *Workspace::findToplevel(std::function func) const { if (Client *ret = Toplevel::findInList(clients, func)) { return ret; } if (Client *ret = Toplevel::findInList(desktops, func)) { return ret; } if (Unmanaged *ret = Toplevel::findInList(unmanaged, func)) { return ret; } return nullptr; } bool Workspace::hasClient(const AbstractClient *c) { if (auto cc = dynamic_cast(c)) { return hasClient(cc); } else { return findAbstractClient([c](const AbstractClient *test) { return test == c; }) != nullptr; } return false; } void Workspace::forEachAbstractClient(std::function< void (AbstractClient*) > func) { std::for_each(m_allClients.constBegin(), m_allClients.constEnd(), func); std::for_each(desktops.constBegin(), desktops.constEnd(), func); } Toplevel *Workspace::findInternal(QWindow *w) const { if (!w) { return nullptr; } if (kwinApp()->operationMode() == Application::OperationModeX11) { return findUnmanaged(w->winId()); } else { return waylandServer()->findClient(w); } } } // namespace