diff --git a/CMakeLists.txt b/CMakeLists.txt index 00d086e5c..d24dbe0f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,624 +1,639 @@ project(KWIN) set(PROJECT_VERSION "5.6.90") set(PROJECT_VERSION_MAJOR 5) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.4.0") set(KF5_MIN_VERSION "5.12.0") set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ) find_package(ECM 0.0.11 REQUIRED NO_MODULE) include(FeatureSummary) include(WriteBasicConfigVersionFile) include(GenerateExportHeader) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Concurrent Core DBus Quick QuickWidgets Script UiTools Widgets X11Extras ) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG QUIET) set_package_properties(Qt5Test PROPERTIES PURPOSE "Required for tests" TYPE OPTIONAL ) add_feature_info("Qt5Test" Qt5Test_FOUND "Required for building tests") if (NOT Qt5Test_FOUND) set(BUILD_TESTING OFF CACHE BOOL "Build the testing tree.") endif() include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMOptionalAddSubdirectory) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0 -DQT_USE_QSTRINGBUILDER) # require at least gcc 4.8 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") if ("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "4.8") message(SEND_ERROR "Version ${CMAKE_CXX_COMPILER_VERSION} of the ${CMAKE_CXX_COMPILER_ID} C++ compiler is not supported. Please use version 4.8 or later.") endif() endif() find_package(Qt5Multimedia QUIET) set_package_properties(Qt5Multimedia PROPERTIES PURPOSE "Runtime-only dependency for effect video playback" TYPE RUNTIME ) # required frameworks by Core find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Config ConfigWidgets CoreAddons Crash GlobalAccel I18n Init Notifications Package Plasma WidgetsAddons WindowSystem IconThemes IdleTime ) # required frameworks by config modules find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Completion Declarative KCMUtils KIO NewStuff Service XmlGui ) find_package(Threads) set_package_properties(Threads PROPERTIES PURPOSE "Needed for VirtualTerminal support in KWin Wayland" TYPE REQUIRED ) # optional frameworks find_package(KF5Activities ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5Activities PROPERTIES PURPOSE "Enable building of KWin with kactivities support" TYPE OPTIONAL ) add_feature_info("KF5Activities" KF5Activities_FOUND "Enable building of KWin with kactivities support") find_package(KF5DocTools ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5DocTools PROPERTIES PURPOSE "Enable building documentation" TYPE OPTIONAL ) add_feature_info("KF5DocTools" KF5DocTools_FOUND "Enable building documentation") find_package(KDecoration2 CONFIG REQUIRED) find_package(KF5Wayland CONFIG REQUIRED) set_package_properties(KF5Wayland PROPERTIES TYPE REQUIRED ) find_package(KScreenLocker CONFIG REQUIRED) set_package_properties(KScreenLocker PROPERTIES TYPE REQUIRED PURPOSE "For screenlocker integration in kwin_wayland") +find_package(Breeze ${PROJECT_VERSION} CONFIG) +set_package_properties(Breeze PROPERTIES + TYPE OPTIONAL + PURPOSE "For setting the default window decoration plugin") +if(${Breeze_FOUND}) + if(${BREEZE_WITH_KDECORATION}) + set(HAVE_BREEZE_DECO true) + else() + set(HAVE_BREEZE_DECO FALSE) + endif() +else() + set(HAVE_BREEZE_DECO FALSE) +endif() +add_feature_info("Breeze-Decoration" HAVE_BREEZE_DECO "Default decoration plugin Breeze") + find_package(EGL) set_package_properties(EGL PROPERTIES TYPE RUNTIME PURPOSE "Required to build KWin with EGL support" ) find_package(epoxy) set_package_properties(epoxy PROPERTIES DESCRIPTION "libepoxy" URL "http://github.com/anholt/libepoxy" TYPE REQUIRED PURPOSE "OpenGL dispatch library" ) find_package(Wayland 1.2 REQUIRED COMPONENTS Cursor OPTIONAL_COMPONENTS Egl) set_package_properties(Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" ) add_feature_info("Wayland::EGL" Wayland_Egl_FOUND "Enable building of Wayland backend and QPA with EGL support.") set(HAVE_WAYLAND_EGL FALSE) if(Wayland_Egl_FOUND) set(HAVE_WAYLAND_EGL TRUE) endif() find_package(XKB 0.4.1) set_package_properties(XKB PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" ) find_package(Libinput 0.10) set_package_properties(Libinput PROPERTIES TYPE OPTIONAL PURPOSE "Required for input handling on Wayland.") find_package(UDev) set_package_properties(UDev PROPERTIES URL "http://www.freedesktop.org/software/systemd/libudev/" DESCRIPTION "Linux device library." TYPE OPTIONAL PURPOSE "Required for input handling on Wayland." ) set(HAVE_INPUT FALSE) if (Libinput_FOUND AND UDEV_FOUND) set(HAVE_INPUT TRUE) endif() find_package(Libdrm) set_package_properties(Libdrm PROPERTIES TYPE OPTIONAL PURPOSE "Required for drm output on Wayland.") set(HAVE_DRM FALSE) if(Libdrm_FOUND AND UDEV_FOUND) set(HAVE_DRM TRUE) endif() find_package(gbm) set_package_properties(gbm PROPERTIES TYPE OPTIONAL PURPOSE "Required for egl ouput of drm backend.") set(HAVE_GBM FALSE) if(HAVE_DRM AND gbm_FOUND) set(HAVE_GBM TRUE) endif() find_package(libhybris) set_package_properties(libhybris PROPERTIES TYPE OPTIONAL PURPOSE "Required for libhybris backend") set(HAVE_LIBHYBRIS ${libhybris_FOUND}) find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE REQUIRED ) add_feature_info("XInput" X11_Xinput_FOUND "Required for poll-free mouse cursor updates") set(HAVE_X11_XINPUT ${X11_Xinput_FOUND}) # All the required XCB components find_package(XCB 1.10 REQUIRED COMPONENTS XCB XFIXES DAMAGE COMPOSITE SHAPE SYNC RENDER RANDR KEYSYMS IMAGE SHM GLX CURSOR OPTIONAL_COMPONENTS ICCCM ) set_package_properties(XCB PROPERTIES TYPE REQUIRED) # and the optional XCB dependencies if (XCB_ICCCM_VERSION VERSION_LESS "0.4") set(XCB_ICCCM_FOUND FALSE) endif() add_feature_info("XCB-ICCCM" XCB_ICCCM_FOUND "Required for building test applications for KWin") find_package(X11_XCB) set_package_properties(X11_XCB PROPERTIES PURPOSE "Required for building X11 windowed backend of kwin_wayland" TYPE OPTIONAL) # dependencies for QPA plugin find_package(Qt5PlatformSupport REQUIRED) find_package(Freetype REQUIRED) set_package_properties(Freetype PROPERTIES DESCRIPTION "A font rendering engine" URL "http://www.freetype.org" TYPE REQUIRED PURPOSE "Needed for KWin's QPA plugin." ) find_package(Fontconfig REQUIRED) set_package_properties(Fontconfig PROPERTIES DESCRIPTION "Font access configuration library" URL "http://www.freedesktop.org/wiki/Software/fontconfig" TYPE REQUIRED PURPOSE "Needed for KWin's QPA plugin." ) find_package(Xwayland) set_package_properties(Xwayland PROPERTIES URL "http://x.org" DESCRIPTION "Xwayland X server" TYPE RUNTIME PURPOSE "Needed for running kwin_wayland" ) ########### configure tests ############### include(CMakeDependentOption) option(KWIN_BUILD_DECORATIONS "Enable building of KWin decorations." ON) option(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON) option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON) option(KWIN_BUILD_XRENDER_COMPOSITING "Enable building of KWin with XRender Compositing support" ON) cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "KF5Activities_FOUND" OFF) # Binary name of KWin set(KWIN_NAME "kwin") set(KWIN_INTERNAL_NAME_X11 "kwin_x11") set(KWIN_INTERNAL_NAME_WAYLAND "kwin_wayland") set(KWIN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # KWIN_HAVE_XRENDER_COMPOSITING - whether XRender-based compositing support is available: may be disabled if( KWIN_BUILD_XRENDER_COMPOSITING ) set( KWIN_HAVE_XRENDER_COMPOSITING 1 ) endif() include_directories(${XKB_INCLUDE_DIR}) include_directories(${epoxy_INCLUDE_DIR}) set(HAVE_EPOXY_GLX ${epoxy_HAS_GLX}) # for things that are also used by kwin libraries configure_file(libkwineffects/kwinconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects/kwinconfig.h ) # for kwin internal things set(HAVE_X11_XCB ${X11_XCB_FOUND}) include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckSymbolExists) check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(malloc.h HAVE_MALLOC_H) check_include_file("sys/prctl.h" HAVE_SYS_PRCTL_H) check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE) add_feature_info("prctl-dumpable" HAVE_PR_SET_DUMPABLE "Required for disallow ptrace on kwin_wayland process") configure_file(config-kwin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwin.h ) ########### global ############### set(kwin_effects_dbus_xml ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.kwin.Effects.xml) include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libkwineffects ${CMAKE_CURRENT_SOURCE_DIR}/effects ${CMAKE_CURRENT_SOURCE_DIR}/tabbox ) add_subdirectory( libkwineffects ) add_subdirectory( killer ) if(KWIN_BUILD_KCMS) add_subdirectory( kcmkwin ) endif() if( KWIN_BUILD_DECORATIONS ) add_subdirectory( clients ) endif() add_subdirectory( data ) add_subdirectory( effects ) add_subdirectory( scripts ) add_subdirectory( tabbox ) add_subdirectory(scripting) add_definitions(-DKDE_DEFAULT_DEBUG_AREA=1212) ########### next target ############### set(kwin_KDEINIT_SRCS workspace.cpp dbusinterface.cpp abstract_client.cpp client.cpp client_machine.cpp cursor.cpp debug_console.cpp tabgroup.cpp focuschain.cpp globalshortcuts.cpp input.cpp keyboard_input.cpp pointer_input.cpp touch_input.cpp netinfo.cpp placement.cpp atoms.cpp utils.cpp layers.cpp main.cpp options.cpp outline.cpp events.cpp killwindow.cpp geometrytip.cpp screens.cpp shadow.cpp sm.cpp group.cpp manage.cpp overlaywindow.cpp activation.cpp useractions.cpp geometry.cpp rules.cpp composite.cpp toplevel.cpp unmanaged.cpp scene.cpp scene_xrender.cpp scene_opengl.cpp scene_qpainter.cpp thumbnailitem.cpp lanczosfilter.cpp deleted.cpp effects.cpp effectloader.cpp compositingprefs.cpp virtualdesktops.cpp xcbutils.cpp x11eventfilter.cpp logind.cpp screenedge.cpp scripting/scripting.cpp scripting/workspace_wrapper.cpp scripting/meta.cpp scripting/scriptedeffect.cpp scripting/scriptingutils.cpp scripting/timer.cpp scripting/scripting_model.cpp scripting/dbuscall.cpp scripting/screenedgeitem.cpp scripting/scripting_logging.cpp decorations/decoratedclient.cpp decorations/decorationbridge.cpp decorations/decorationpalette.cpp decorations/settings.cpp decorations/decorationrenderer.cpp decorations/decorations_logging.cpp abstract_egl_backend.cpp eglonxbackend.cpp platform.cpp shell_client.cpp wayland_server.cpp wayland_cursor_theme.cpp ) if(HAVE_EPOXY_GLX) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} glxbackend.cpp) endif() if(KWIN_BUILD_TABBOX) set( kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} tabbox/tabbox.cpp tabbox/clientmodel.cpp tabbox/desktopchain.cpp tabbox/desktopmodel.cpp tabbox/switcheritem.cpp tabbox/tabboxconfig.cpp tabbox/tabboxhandler.cpp tabbox/tabbox_logging.cpp ) endif() if(KWIN_BUILD_ACTIVITIES) set( kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} activities.cpp ) endif() if(UDEV_FOUND) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} udev.cpp ) endif() if(HAVE_INPUT) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} libinput/context.cpp libinput/connection.cpp libinput/events.cpp libinput/libinput_logging.cpp virtual_terminal.cpp ) endif() kconfig_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml dbusinterface.h KWin::DBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.Compositing.xml dbusinterface.h KWin::CompositorDBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl ) qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.ScreenSaver.xml screenlocker_interface) qt5_add_resources( kwin_KDEINIT_SRCS resources.qrc ) ki18n_wrap_ui(kwin_KDEINIT_SRCS debug_console.ui shortcutdialog.ui ) ########### target link libraries ############### set(kwin_OWN_LIBS kwineffects kwinxrenderutils kwin4_effect_builtins ) set(kwin_QT_LIBS Qt5::Concurrent Qt5::DBus Qt5::Quick Qt5::Script Qt5::X11Extras ) set(kwin_KDE_LIBS KF5::ConfigCore KF5::CoreAddons KF5::ConfigWidgets KF5::Crash KF5::GlobalAccel KF5::GlobalAccelPrivate KF5::I18n KF5::Notifications KF5::Package KF5::Plasma KF5::WindowSystem KDecoration2::KDecoration KDecoration2::KDecoration2Private PW::KScreenLocker ) set(kwin_XLIB_LIBS ${X11_X11_LIB} ${X11_ICE_LIB} ${X11_SM_LIB} ) if(X11_Xinput_FOUND) set(kwin_XLIB_LIBS ${kwin_XLIB_LIBS} ${X11_Xinput_LIB}) endif() set(kwin_XCB_LIBS XCB::XCB XCB::XFIXES XCB::DAMAGE XCB::COMPOSITE XCB::SHAPE XCB::SYNC XCB::RENDER XCB::RANDR XCB::KEYSYMS XCB::SHM XCB::GLX XCB::CURSOR ) set(kwin_WAYLAND_LIBS XKB::XKB KF5::WaylandClient KF5::WaylandServer Wayland::Cursor ${CMAKE_THREAD_LIBS_INIT} ) if(KWIN_BUILD_ACTIVITIES) set(kwin_KDE_LIBS ${kwin_KDE_LIBS} KF5::Activities) endif() set(kwinLibs ${kwin_OWN_LIBS} ${kwin_QT_LIBS} ${kwin_KDE_LIBS} ${kwin_XLIB_LIBS} ${kwin_XCB_LIBS} ${kwin_WAYLAND_LIBS} ) if(UDEV_FOUND) set(kwinLibs ${kwinLibs} ${UDEV_LIBS}) endif() if(HAVE_INPUT) set(kwinLibs ${kwinLibs} Libinput::Libinput) endif() add_library(kwin SHARED ${kwin_KDEINIT_SRCS}) set_target_properties(kwin PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) target_link_libraries(kwin ${kwinLibs}) generate_export_header(kwin EXPORT_FILE_NAME kwin_export.h) target_link_libraries(kwin kwinglutils ${epoxy_LIBRARY}) # -ldl used by OpenGL code find_library(DL_LIBRARY dl) if (DL_LIBRARY) target_link_libraries(kwin ${DL_LIBRARY}) endif() kf5_add_kdeinit_executable(kwin_x11 main_x11.cpp) target_link_libraries(kdeinit_kwin_x11 kwin) install(TARGETS kwin ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP ) install(TARGETS kdeinit_kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(TARGETS kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) add_executable(kwin_wayland main_wayland.cpp) target_link_libraries(kwin_wayland kwin) install(TARGETS kwin_wayland ${INSTALL_TARGETS_DEFAULT_ARGS} ) add_subdirectory(plugins) ########### install files ############### install( FILES kwin.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}.kcfg ) install( FILES kwin.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR} RENAME ${KWIN_NAME}.notifyrc) install( FILES org.kde.KWin.xml org.kde.kwin.Compositing.xml org.kde.kwin.Effects.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kwin_export.h DESTINATION ${INCLUDE_INSTALL_DIR} COMPONENT Devel) # Install the KWin/Script service type install( FILES scripting/kwinscript.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) ecm_install_icons( ICONS 16-apps-kwin.png 32-apps-kwin.png 48-apps-kwin.png sc-apps-kwin.svgz DESTINATION ${ICON_INSTALL_DIR} THEME hicolor ) add_subdirectory(qml) add_subdirectory(autotests) add_subdirectory(tests) if (KF5DocTools_FOUND) add_subdirectory(doc) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) include(ECMPackageConfigHelpers) set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KWinDBusInterface") ecm_configure_package_config_file(KWinDBusInterfaceConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/KWinDBusInterfaceConfig.cmake" PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KWinDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake index 0996173d8..c7d741795 100644 --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -1,32 +1,36 @@ #cmakedefine KWIN_BUILD_DECORATIONS 1 #cmakedefine KWIN_BUILD_TABBOX 1 #cmakedefine KWIN_BUILD_ACTIVITIES 1 #define KWIN_NAME "${KWIN_NAME}" #define KWIN_INTERNAL_NAME_X11 "${KWIN_INTERNAL_NAME_X11}" #define KWIN_CONFIG "${KWIN_NAME}rc" #define KWIN_VERSION_STRING "${PROJECT_VERSION}" #define XCB_VERSION_STRING "${XCB_VERSION}" #define KWIN_KILLER_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kwin_killer_helper" #define KWIN_RULES_DIALOG_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kwin_rules_dialog" #cmakedefine01 HAVE_INPUT #cmakedefine01 HAVE_X11_XCB #cmakedefine01 HAVE_X11_XINPUT #cmakedefine01 HAVE_DRM #cmakedefine01 HAVE_GBM #cmakedefine01 HAVE_LIBHYBRIS #cmakedefine01 HAVE_WAYLAND_EGL #cmakedefine01 HAVE_SYS_PRCTL_H #cmakedefine01 HAVE_PR_SET_DUMPABLE +#cmakedefine01 HAVE_BREEZE_DECO +#if HAVE_BREEZE_DECO +#define BREEZE_KDECORATION_PLUGIN_ID "${BREEZE_KDECORATION_PLUGIN_ID}" +#endif /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MALLOC_H 1 #cmakedefine XCB_ICCCM_FOUND 1 #ifndef XCB_ICCCM_FOUND #define XCB_ICCCM_WM_STATE_WITHDRAWN 0 #define XCB_ICCCM_WM_STATE_NORMAL 1 #define XCB_ICCCM_WM_STATE_ICONIC 3 #endif diff --git a/decorations/decorationbridge.cpp b/decorations/decorationbridge.cpp index e4762dec0..5afdbdfbf 100644 --- a/decorations/decorationbridge.cpp +++ b/decorations/decorationbridge.cpp @@ -1,295 +1,301 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 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 "decorationbridge.h" #include "decoratedclient.h" #include "decorationrenderer.h" #include "decorations_logging.h" #include "settings.h" // KWin core #include "abstract_client.h" #include "composite.h" #include "scene.h" #include "wayland_server.h" #include "workspace.h" +#include // KDecoration #include #include #include // KWayland #include // Frameworks #include #include // Qt #include #include namespace KWin { namespace Decoration { +static const QString s_aurorae = QStringLiteral("org.kde.kwin.aurorae"); static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); -static const QString s_defaultPlugin = QStringLiteral("org.kde.breeze"); +#if HAVE_BREEZE_DECO +static const QString s_defaultPlugin = QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID); +#else +static const QString s_defaultPlugin = s_aurorae; +#endif KWIN_SINGLETON_FACTORY(DecorationBridge) DecorationBridge::DecorationBridge(QObject *parent) : KDecoration2::DecorationBridge(parent) , m_factory(nullptr) , m_blur(false) , m_settings() , m_noPlugin(false) { } DecorationBridge::~DecorationBridge() { s_self = nullptr; } static QString readPlugin() { return kwinApp()->config()->group(s_pluginName).readEntry("library", s_defaultPlugin); } static bool readNoPlugin() { return kwinApp()->config()->group(s_pluginName).readEntry("NoPlugin", false); } QString DecorationBridge::readTheme() const { return kwinApp()->config()->group(s_pluginName).readEntry("theme", m_defaultTheme); } void DecorationBridge::init() { using namespace KWayland::Server; m_noPlugin = readNoPlugin(); if (m_noPlugin) { if (waylandServer()) { waylandServer()->decorationManager()->setDefaultMode(ServerSideDecorationManagerInterface::Mode::None); } return; } m_plugin = readPlugin(); m_settings = QSharedPointer::create(this); initPlugin(); if (!m_factory) { if (m_plugin != s_defaultPlugin) { // try loading default plugin m_plugin = s_defaultPlugin; initPlugin(); } // default plugin failed to load, try fallback if (!m_factory) { - m_plugin = QStringLiteral("org.kde.kwin.aurorae"); + m_plugin = s_aurorae; initPlugin(); } } if (waylandServer()) { waylandServer()->decorationManager()->setDefaultMode(m_factory ? ServerSideDecorationManagerInterface::Mode::Server : ServerSideDecorationManagerInterface::Mode::None); } } void DecorationBridge::initPlugin() { const auto offers = KPluginLoader::findPluginsById(s_pluginName, m_plugin); if (offers.isEmpty()) { qCWarning(KWIN_DECORATIONS) << "Could not locate decoration plugin"; return; } qCDebug(KWIN_DECORATIONS) << "Trying to load decoration plugin: " << offers.first().fileName(); KPluginLoader loader(offers.first().fileName()); KPluginFactory *factory = loader.factory(); if (!factory) { qCWarning(KWIN_DECORATIONS) << "Error loading plugin:" << loader.errorString(); } else { m_factory = factory; loadMetaData(loader.metaData().value(QStringLiteral("MetaData")).toObject()); } } static void recreateDecorations() { Workspace::self()->forEachAbstractClient([](AbstractClient *c) { c->updateDecoration(true, true); }); } void DecorationBridge::reconfigure() { if (m_noPlugin != readNoPlugin()) { m_noPlugin = !m_noPlugin; // no plugin setting changed if (m_noPlugin) { // decorations disabled now m_plugin = QString(); delete m_factory; m_factory = nullptr; m_settings.clear(); } else { // decorations enabled now init(); } recreateDecorations(); return; } const QString newPlugin = readPlugin(); if (newPlugin != m_plugin) { // plugin changed, recreate everything auto oldFactory = m_factory; const auto oldPluginName = m_plugin; m_plugin = newPlugin; initPlugin(); if (m_factory == oldFactory) { // loading new plugin failed m_factory = oldFactory; m_plugin = oldPluginName; } else { recreateDecorations(); // TODO: unload and destroy old plugin } } else { // same plugin, but theme might have changed const QString oldTheme = m_theme; m_theme = readTheme(); if (m_theme != oldTheme) { recreateDecorations(); } } } void DecorationBridge::loadMetaData(const QJsonObject &object) { // reset all settings m_blur = false; m_theme = QString(); m_defaultTheme = QString(); // load the settings const QJsonValue decoSettings = object.value(s_pluginName); if (decoSettings.isUndefined()) { // no settings return; } const QVariantMap decoSettingsMap = decoSettings.toObject().toVariantMap(); auto blurIt = decoSettingsMap.find(QStringLiteral("blur")); if (blurIt != decoSettingsMap.end()) { m_blur = blurIt.value().toBool(); } findTheme(decoSettingsMap); } void DecorationBridge::findTheme(const QVariantMap &map) { auto it = map.find(QStringLiteral("themes")); if (it == map.end()) { return; } if (!it.value().toBool()) { return; } it = map.find(QStringLiteral("defaultTheme")); m_defaultTheme = it != map.end() ? it.value().toString() : QString(); m_theme = readTheme(); } std::unique_ptr DecorationBridge::createClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration) { return std::unique_ptr(new DecoratedClientImpl(static_cast(decoration->parent()), client, decoration)); } std::unique_ptr DecorationBridge::settings(KDecoration2::DecorationSettings *parent) { return std::unique_ptr(new SettingsImpl(parent)); } void DecorationBridge::update(KDecoration2::Decoration *decoration, const QRect &geometry) { // TODO: remove check once all compositors implement it if (AbstractClient *c = Workspace::self()->findAbstractClient([decoration] (const AbstractClient *client) { return client->decoration() == decoration; })) { if (Renderer *renderer = c->decoratedClient()->renderer()) { renderer->schedule(geometry); } } } KDecoration2::Decoration *DecorationBridge::createDecoration(AbstractClient *client) { if (m_noPlugin) { return nullptr; } if (!m_factory) { return nullptr; } QVariantMap args({ {QStringLiteral("bridge"), QVariant::fromValue(this)} }); if (!m_theme.isEmpty()) { args.insert(QStringLiteral("theme"), m_theme); } auto deco = m_factory->create(client, QVariantList({args})); deco->setSettings(m_settings); deco->init(); return deco; } static QString settingsProperty(const QVariant &variant) { if (QLatin1String(variant.typeName()) == QLatin1String("KDecoration2::BorderSize")) { return QString::number(variant.toInt()); } else if (QLatin1String(variant.typeName()) == QLatin1String("QVector")) { const auto &b = variant.value>(); QString buffer; for (auto it = b.begin(); it != b.end(); ++it) { if (it != b.begin()) { buffer.append(QStringLiteral(", ")); } buffer.append(QString::number(int(*it))); } return buffer; } return variant.toString(); } QString DecorationBridge::supportInformation() const { QString b; b.append(QStringLiteral("Plugin: %1\n").arg(m_plugin)); b.append(QStringLiteral("Theme: %1\n").arg(m_theme)); b.append(QStringLiteral("Blur: %1\n").arg(m_blur)); const QMetaObject *metaOptions = m_settings->metaObject(); for (int i=0; ipropertyCount(); ++i) { const QMetaProperty property = metaOptions->property(i); if (QLatin1String(property.name()) == QLatin1String("objectName")) { continue; } b.append(QStringLiteral("%1: %2\n").arg(property.name()).arg(settingsProperty(m_settings->property(property.name())))); } return b; } } // Decoration } // KWin diff --git a/kcmkwin/kwindecoration/kcm.cpp b/kcmkwin/kwindecoration/kcm.cpp index fefdbe346..035bc8b2a 100644 --- a/kcmkwin/kwindecoration/kcm.cpp +++ b/kcmkwin/kwindecoration/kcm.cpp @@ -1,438 +1,445 @@ /* * Copyright 2014 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "kcm.h" #include "decorationmodel.h" #include "declarative-plugin/buttonsmodel.h" +#include // KDE #include #include #include #include #include #include // Qt #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(KDecorationFactory, registerPlugin(); ) Q_DECLARE_METATYPE(KDecoration2::BorderSize) namespace KDecoration2 { namespace Configuration { static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); -static const QString s_defaultPlugin = QStringLiteral("org.kde.breeze"); +#if HAVE_BREEZE_DECO +static const QString s_defaultPlugin = QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID); +static const QString s_defaultTheme; +#else +static const QString s_defaultPlugin = QStringLiteral("org.kde.kwin.aurorae"); +static const QString s_defaultTheme = QStringLiteral("kwin4_decoration_qml_plastik"); +#endif static const QString s_borderSizeNormal = QStringLiteral("Normal"); static const QString s_ghnsIcon = QStringLiteral("get-hot-new-stuff"); ConfigurationForm::ConfigurationForm(QWidget *parent) : QWidget(parent) { setupUi(this); } static bool s_loading = false; ConfigurationModule::ConfigurationModule(QWidget *parent, const QVariantList &args) : KCModule(parent, args) , m_model(new DecorationsModel(this)) , m_proxyModel(new QSortFilterProxyModel(this)) , m_ui(new ConfigurationForm(this)) , m_leftButtons(new Preview::ButtonsModel(QVector(), this)) , m_rightButtons(new Preview::ButtonsModel(QVector(), this)) , m_availableButtons(new Preview::ButtonsModel(this)) { m_proxyModel->setSourceModel(m_model); m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); connect(m_ui->filter, &QLineEdit::textChanged, m_proxyModel, &QSortFilterProxyModel::setFilterFixedString); m_quickView = new QQuickView(0); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(m_quickView->engine()); kdeclarative.setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); kdeclarative.setupBindings(); qmlRegisterType(); QWidget *widget = QWidget::createWindowContainer(m_quickView, this); QVBoxLayout* layout = new QVBoxLayout(m_ui->view); layout->setContentsMargins(0,0,0,0); layout->addWidget(widget); m_quickView->rootContext()->setContextProperty(QStringLiteral("decorationsModel"), m_proxyModel); updateColors(); m_quickView->rootContext()->setContextProperty("_borderSizesIndex", 3); // 3 is normal m_quickView->rootContext()->setContextProperty("configurationModule", this); m_quickView->rootContext()->setContextProperty("titleFont", QFontDatabase::systemFont(QFontDatabase::TitleFont)); m_quickView->setResizeMode(QQuickView::SizeRootObjectToView); m_quickView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/kcm_kwindecoration/main.qml")))); if (m_quickView->status() == QQuickView::Ready) { auto listView = m_quickView->rootObject()->findChild("listView"); if (listView) { connect(listView, SIGNAL(currentIndexChanged()), this, SLOT(changed())); } } m_ui->tabWidget->tabBar()->disconnect(); auto setCurrentTab = [this](int index) { if (index == 0) m_ui->doubleClickMessage->hide(); m_ui->filter->setVisible(index == 0); m_ui->knsButton->setVisible(index == 0); if (auto themeList = m_quickView->rootObject()->findChild("themeList")) { themeList->setVisible(index == 0); } m_ui->borderSizesLabel->setVisible(index == 0); m_ui->borderSizesCombo->setVisible(index == 0); m_ui->closeWindowsDoubleClick->setVisible(index == 1); if (auto buttonLayout = m_quickView->rootObject()->findChild("buttonLayout")) { buttonLayout->setVisible(index == 1); } }; connect(m_ui->tabWidget->tabBar(), &QTabBar::currentChanged, this, setCurrentTab); setCurrentTab(0); m_ui->doubleClickMessage->setVisible(false); m_ui->doubleClickMessage->setText(i18n("Close by double clicking:\n To open the menu, keep the button pressed until it appears.")); m_ui->doubleClickMessage->setCloseButtonVisible(true); m_ui->borderSizesCombo->setItemData(0, QVariant::fromValue(BorderSize::None)); m_ui->borderSizesCombo->setItemData(1, QVariant::fromValue(BorderSize::NoSides)); m_ui->borderSizesCombo->setItemData(2, QVariant::fromValue(BorderSize::Tiny)); m_ui->borderSizesCombo->setItemData(3, QVariant::fromValue(BorderSize::Normal)); m_ui->borderSizesCombo->setItemData(4, QVariant::fromValue(BorderSize::Large)); m_ui->borderSizesCombo->setItemData(5, QVariant::fromValue(BorderSize::VeryLarge)); m_ui->borderSizesCombo->setItemData(6, QVariant::fromValue(BorderSize::Huge)); m_ui->borderSizesCombo->setItemData(7, QVariant::fromValue(BorderSize::VeryHuge)); m_ui->borderSizesCombo->setItemData(8, QVariant::fromValue(BorderSize::Oversized)); m_ui->knsButton->setIcon(QIcon::fromTheme(s_ghnsIcon)); auto changedSlot = static_cast(&ConfigurationModule::changed); connect(m_ui->closeWindowsDoubleClick, &QCheckBox::stateChanged, this, changedSlot); connect(m_ui->closeWindowsDoubleClick, &QCheckBox::toggled, this, [this] (bool toggled) { if (s_loading) { return; } if (toggled) m_ui->doubleClickMessage->animatedShow(); else m_ui->doubleClickMessage->animatedHide(); } ); connect(m_ui->borderSizesCombo, static_cast(&QComboBox::currentIndexChanged), this, [this] (int index) { auto listView = m_quickView->rootObject()->findChild("listView"); if (listView) { listView->setProperty("borderSizesIndex", index); } changed(); } ); connect(m_model, &QAbstractItemModel::modelReset, this, [this] { const auto &kns = m_model->knsProviders(); m_ui->knsButton->setEnabled(!kns.isEmpty()); if (kns.isEmpty()) { return; } if (kns.count() > 1) { QMenu *menu = new QMenu(m_ui->knsButton); for (auto it = kns.begin(); it != kns.end(); ++it) { QAction *action = menu->addAction(QIcon::fromTheme(s_ghnsIcon), it.value()); action->setData(it.key()); connect(action, &QAction::triggered, this, [this, action] { showKNS(action->data().toString());}); } m_ui->knsButton->setMenu(menu); } } ); connect(m_ui->knsButton, &QPushButton::clicked, this, [this] { const auto &kns = m_model->knsProviders(); if (kns.isEmpty()) { return; } showKNS(kns.firstKey()); } ); connect(m_leftButtons, &QAbstractItemModel::rowsInserted, this, changedSlot); connect(m_leftButtons, &QAbstractItemModel::rowsMoved, this, changedSlot); connect(m_leftButtons, &QAbstractItemModel::rowsRemoved, this, changedSlot); connect(m_rightButtons, &QAbstractItemModel::rowsInserted, this, changedSlot); connect(m_rightButtons, &QAbstractItemModel::rowsMoved, this, changedSlot); connect(m_rightButtons, &QAbstractItemModel::rowsRemoved, this, changedSlot); QVBoxLayout *l = new QVBoxLayout(this); l->addWidget(m_ui); QMetaObject::invokeMethod(m_model, "init", Qt::QueuedConnection); m_ui->installEventFilter(this); } ConfigurationModule::~ConfigurationModule() = default; void ConfigurationModule::showEvent(QShowEvent *ev) { KCModule::showEvent(ev); } static const QMap s_sizes = QMap({ {QStringLiteral("None"), BorderSize::None}, {QStringLiteral("NoSides"), BorderSize::NoSides}, {QStringLiteral("Tiny"), BorderSize::Tiny}, {s_borderSizeNormal, BorderSize::Normal}, {QStringLiteral("Large"), BorderSize::Large}, {QStringLiteral("VeryLarge"), BorderSize::VeryLarge}, {QStringLiteral("Huge"), BorderSize::Huge}, {QStringLiteral("VeryHuge"), BorderSize::VeryHuge}, {QStringLiteral("Oversized"), BorderSize::Oversized} }); static BorderSize stringToSize(const QString &name) { auto it = s_sizes.constFind(name); if (it == s_sizes.constEnd()) { // non sense values are interpreted just like normal return BorderSize::Normal; } return it.value(); } static QString sizeToString(BorderSize size) { return s_sizes.key(size, s_borderSizeNormal); } static QHash s_buttonNames; static void initButtons() { if (!s_buttonNames.isEmpty()) { return; } s_buttonNames[KDecoration2::DecorationButtonType::Menu] = QChar('M'); s_buttonNames[KDecoration2::DecorationButtonType::ApplicationMenu] = QChar('N'); s_buttonNames[KDecoration2::DecorationButtonType::OnAllDesktops] = QChar('S'); s_buttonNames[KDecoration2::DecorationButtonType::ContextHelp] = QChar('H'); s_buttonNames[KDecoration2::DecorationButtonType::Minimize] = QChar('I'); s_buttonNames[KDecoration2::DecorationButtonType::Maximize] = QChar('A'); s_buttonNames[KDecoration2::DecorationButtonType::Close] = QChar('X'); s_buttonNames[KDecoration2::DecorationButtonType::KeepAbove] = QChar('F'); s_buttonNames[KDecoration2::DecorationButtonType::KeepBelow] = QChar('B'); s_buttonNames[KDecoration2::DecorationButtonType::Shade] = QChar('L'); } static QString buttonsToString(const QVector &buttons) { auto buttonToString = [](KDecoration2::DecorationButtonType button) -> QChar { const auto it = s_buttonNames.constFind(button); if (it != s_buttonNames.constEnd()) { return it.value(); } return QChar(); }; QString ret; for (auto button : buttons) { ret.append(buttonToString(button)); } return ret; } static QVector< KDecoration2::DecorationButtonType > readDecorationButtons(const KConfigGroup &config, const char *key, const QVector< KDecoration2::DecorationButtonType > &defaultValue) { initButtons(); auto buttonsFromString = [](const QString &buttons) -> QVector { QVector ret; for (auto it = buttons.begin(); it != buttons.end(); ++it) { for (auto it2 = s_buttonNames.constBegin(); it2 != s_buttonNames.constEnd(); ++it2) { if (it2.value() == (*it)) { ret << it2.key(); } } } return ret; }; return buttonsFromString(config.readEntry(key, buttonsToString(defaultValue))); } void ConfigurationModule::load() { s_loading = true; const KConfigGroup config = KSharedConfig::openConfig("kwinrc")->group(s_pluginName); const QString plugin = config.readEntry("library", s_defaultPlugin); - const QString theme = config.readEntry("theme", QString()); + const QString theme = config.readEntry("theme", s_defaultTheme); const QModelIndex index = m_proxyModel->mapFromSource(m_model->findDecoration(plugin, theme)); if (auto listView = m_quickView->rootObject()->findChild("listView")) { listView->setProperty("currentIndex", index.isValid() ? index.row() : -1); } m_ui->closeWindowsDoubleClick->setChecked(config.readEntry("CloseOnDoubleClickOnMenu", false)); const QVariant border = QVariant::fromValue(stringToSize(config.readEntry("BorderSize", s_borderSizeNormal))); m_ui->borderSizesCombo->setCurrentIndex(m_ui->borderSizesCombo->findData(border)); // buttons const auto &left = readDecorationButtons(config, "ButtonsOnLeft", QVector{ KDecoration2::DecorationButtonType::Menu, KDecoration2::DecorationButtonType::OnAllDesktops }); while (m_leftButtons->rowCount() > 0) { m_leftButtons->remove(0); } for (auto it = left.begin(); it != left.end(); ++it) { m_leftButtons->add(*it); } const auto &right = readDecorationButtons(config, "ButtonsOnRight", QVector{ KDecoration2::DecorationButtonType::ContextHelp, KDecoration2::DecorationButtonType::Minimize, KDecoration2::DecorationButtonType::Maximize, KDecoration2::DecorationButtonType::Close }); while (m_rightButtons->rowCount() > 0) { m_rightButtons->remove(0); } for (auto it = right.begin(); it != right.end(); ++it) { m_rightButtons->add(*it); } KCModule::load(); s_loading = false; } void ConfigurationModule::save() { KConfigGroup config = KSharedConfig::openConfig("kwinrc")->group(s_pluginName); config.writeEntry("CloseOnDoubleClickOnMenu", m_ui->closeWindowsDoubleClick->isChecked()); config.writeEntry("BorderSize", sizeToString(m_ui->borderSizesCombo->currentData().value())); if (auto listView = m_quickView->rootObject()->findChild("listView")) { const int currentIndex = listView->property("currentIndex").toInt(); if (currentIndex != -1) { const QModelIndex index = m_proxyModel->index(currentIndex, 0); if (index.isValid()) { config.writeEntry("library", index.data(Qt::UserRole + 4).toString()); const QString theme = index.data(Qt::UserRole +5).toString(); if (theme.isEmpty()) { config.deleteEntry("theme"); } else { config.writeEntry("theme", theme); } } } } config.writeEntry("ButtonsOnLeft", buttonsToString(m_leftButtons->buttons())); config.writeEntry("ButtonsOnRight", buttonsToString(m_rightButtons->buttons())); config.sync(); KCModule::save(); // Send signal to all kwin instances QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); QDBusConnection::sessionBus().send(message); } void ConfigurationModule::defaults() { if (auto listView = m_quickView->rootObject()->findChild("listView")) { const QModelIndex index = m_proxyModel->mapFromSource(m_model->findDecoration(s_defaultPlugin)); listView->setProperty("currentIndex", index.isValid() ? index.row() : -1); } m_ui->borderSizesCombo->setCurrentIndex(m_ui->borderSizesCombo->findData(QVariant::fromValue(stringToSize(s_borderSizeNormal)))); m_ui->closeWindowsDoubleClick->setChecked(false); KCModule::defaults(); } void ConfigurationModule::showKNS(const QString &config) { QPointer downloadDialog = new KNS3::DownloadDialog(config, this); if (downloadDialog->exec() == QDialog::Accepted && !downloadDialog->changedEntries().isEmpty()) { auto listView = m_quickView->rootObject()->findChild("listView"); QString selectedPluginName; QString selectedThemeName; if (listView) { const QModelIndex index = m_proxyModel->index(listView->property("currentIndex").toInt(), 0); if (index.isValid()) { selectedPluginName = index.data(Qt::UserRole + 4).toString(); selectedThemeName = index.data(Qt::UserRole + 5).toString(); } } m_model->init(); if (!selectedPluginName.isEmpty()) { const QModelIndex index = m_model->findDecoration(selectedPluginName, selectedThemeName); const QModelIndex proxyIndex = m_proxyModel->mapFromSource(index); if (listView) { listView->setProperty("currentIndex", proxyIndex.isValid() ? proxyIndex.row() : -1); } } } delete downloadDialog; } QAbstractItemModel *ConfigurationModule::leftButtons() const { return m_leftButtons; } QAbstractItemModel *ConfigurationModule::rightButtons() const { return m_rightButtons; } QAbstractItemModel *ConfigurationModule::availableButtons() const { return m_availableButtons; } bool ConfigurationModule::eventFilter(QObject *watched, QEvent *e) { if (watched != m_ui) { return false; } if (e->type() == QEvent::PaletteChange) { updateColors(); } return false; } void ConfigurationModule::updateColors() { m_quickView->rootContext()->setContextProperty("backgroundColor", m_ui->palette().color(QPalette::Active, QPalette::Window)); m_quickView->rootContext()->setContextProperty("highlightColor", m_ui->palette().color(QPalette::Active, QPalette::Highlight)); m_quickView->rootContext()->setContextProperty("baseColor", m_ui->palette().color(QPalette::Active, QPalette::Base)); } } } #include "kcm.moc"