diff --git a/CMakeLists.txt b/CMakeLists.txt index 074d0263e..367e6208f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,720 +1,721 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(KWIN) set(PROJECT_VERSION "5.14.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.11.0") set(KF5_MIN_VERSION "5.42.0") set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ) find_package(ECM 5.38 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 Sensors 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) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override") 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 Wayland ) # required frameworks by config modules find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Completion Declarative KCMUtils KIO TextWidgets 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 5.13.0 CONFIG REQUIRED) find_package(KScreenLocker CONFIG REQUIRED) set_package_properties(KScreenLocker PROPERTIES TYPE REQUIRED PURPOSE "For screenlocker integration in kwin_wayland") find_package(Breeze 5.9.0 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" ) set(HAVE_DL_LIBRARY FALSE) if(epoxy_HAS_GLX) find_library(DL_LIBRARY dl) if(DL_LIBRARY) set(HAVE_DL_LIBRARY TRUE) endif() endif() 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.7.0) set_package_properties(XKB PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" ) find_package(Libinput 1.9) set_package_properties(Libinput PROPERTIES TYPE REQUIRED 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 REQUIRED PURPOSE "Required for input handling on Wayland." ) find_package(Libdrm 2.4.62) set_package_properties(Libdrm PROPERTIES TYPE OPTIONAL PURPOSE "Required for drm output on Wayland.") set(HAVE_DRM FALSE) if(Libdrm_FOUND) set(HAVE_DRM TRUE) endif() find_package(gbm) set_package_properties(gbm PROPERTIES TYPE OPTIONAL PURPOSE "Required for egl output 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 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(Qt5FontDatabaseSupport REQUIRED) find_package(Qt5ThemeSupport REQUIRED) find_package(Qt5EventDispatcherSupport 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" ) find_package(Libcap) set_package_properties(Libcap PROPERTIES TYPE OPTIONAL PURPOSE "Needed for running kwin_wayland with real-time scheduling policy" ) set(HAVE_LIBCAP ${Libcap_FOUND}) include(ECMQMLModules) ecm_find_qmlmodule(QtQuick 2.3) ecm_find_qmlmodule(QtQuick.Controls 1.2) ecm_find_qmlmodule(QtQuick.Layouts 1.3) ecm_find_qmlmodule(QtQuick.VirtualKeyboard 2.1) ecm_find_qmlmodule(QtQuick.Window 2.1) ecm_find_qmlmodule(QtMultimedia 5.0) ecm_find_qmlmodule(org.kde.kquickcontrolsaddons 2.0) ecm_find_qmlmodule(org.kde.plasma.core 2.0) ecm_find_qmlmodule(org.kde.plasma.components 2.0) ########### 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) check_symbol_exists(PR_SET_PDEATHSIG "sys/prctl.h" HAVE_PR_SET_PDEATHSIG) check_include_file("sys/procctl.h" HAVE_SYS_PROCCTL_H) check_symbol_exists(PROC_TRACE_CTL "sys/procctl.h" HAVE_PROC_TRACE_CTL) if (HAVE_PR_SET_DUMPABLE OR HAVE_PROC_TRACE_CTL) set(CAN_DISABLE_PTRACE TRUE) endif() add_feature_info("prctl/procctl tracing control" CAN_DISABLE_PTRACE "Required for disallowing ptrace on kwin_wayland process") check_include_file("sys/sysmacros.h" HAVE_SYS_SYSMACROS_H) configure_file(config-kwin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwin.h ) check_include_file("linux/vt.h" HAVE_LINUX_VT_H) add_feature_info("linux/vt.h" HAVE_LINUX_VT_H "Required for virtual terminal support under wayland") check_include_file("linux/fb.h" HAVE_LINUX_FB_H) add_feature_info("linux/fb.h" HAVE_LINUX_FB_H "Required for the fbdev backend") check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK) add_feature_info("SCHED_RESET_ON_FORK" HAVE_SCHED_RESET_ON_FORK "Required for running kwin_wayland with real-time scheduling") ########### 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 ${CMAKE_CURRENT_SOURCE_DIR}/platformsupport ) add_subdirectory( libkwineffects ) if(KWIN_BUILD_KCMS) add_subdirectory( kcmkwin ) endif() add_subdirectory( data ) add_subdirectory( effects ) add_subdirectory( scripts ) add_subdirectory( tabbox ) add_subdirectory(scripting) add_subdirectory(helpers) ########### next target ############### set(kwin_KDEINIT_SRCS workspace.cpp dbusinterface.cpp virtualdesktopsdbustypes.cpp abstract_client.cpp client.cpp client_machine.cpp cursor.cpp debug_console.cpp tabgroup.cpp focuschain.cpp globalshortcuts.cpp input.cpp input_event.cpp input_event_spy.cpp keyboard_input.cpp keyboard_layout.cpp keyboard_layout_switching.cpp keyboard_repeat.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 outputscreens.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 screenlockerwatcher.cpp thumbnailitem.cpp lanczosfilter.cpp deleted.cpp effects.cpp effectloader.cpp virtualdesktops.cpp xcbutils.cpp x11eventfilter.cpp logind.cpp onscreennotification.cpp osd.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 platform.cpp abstract_output.cpp shell_client.cpp wayland_server.cpp wayland_cursor_theme.cpp virtualkeyboard.cpp virtualkeyboard_dbus.cpp appmenu.cpp modifier_only_shortcuts.cpp xkb.cpp gestures.cpp popup_input_filter.cpp colorcorrection/manager.cpp colorcorrection/colorcorrectdbusinterface.cpp colorcorrection/suncalc.cpp abstract_opengl_context_attribute_builder.cpp egl_context_attribute_builder.cpp was_user_interaction_x11_filter.cpp moving_client_x11_filter.cpp window_property_notify_x11_filter.cpp rootinfo_filter.cpp orientation_sensor.cpp idle_inhibition.cpp libinput/context.cpp libinput/connection.cpp libinput/device.cpp libinput/events.cpp libinput/libinput_logging.cpp udev.cpp ) include(ECMQtDeclareLoggingCategory) ecm_qt_declare_logging_category(kwin_KDEINIT_SRCS HEADER colorcorrect_logging.h IDENTIFIER KWIN_COLORCORRECTION CATEGORY_NAME kwin_colorcorrection DEFAULT_SEVERITY Critical ) if(KWIN_BUILD_TABBOX) + include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 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 tabbox/x11_filter.cpp ) endif() if(KWIN_BUILD_ACTIVITIES) set( kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} activities.cpp ) endif() if (HAVE_LINUX_VT_H) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} virtual_terminal.cpp ) endif() kconfig_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc) kconfig_add_kcfg_files(kwin_KDEINIT_SRCS colorcorrection/colorcorrect_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 org.kde.kwin.ColorCorrect.xml colorcorrection/colorcorrectdbusinterface.h KWin::ColorCorrect::ColorCorrectDBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.OrientationSensor.xml orientation_sensor.h KWin::OrientationSensor) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface ) qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.ScreenSaver.xml screenlocker_interface) qt5_add_dbus_interface( kwin_KDEINIT_SRCS org.kde.kappmenu.xml appmenu_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 kwin4_effect_builtins ) set(kwin_QT_LIBS Qt5::Concurrent Qt5::DBus Qt5::Quick Qt5::Sensors Qt5::Script ) set(kwin_KDE_LIBS KF5::ConfigCore KF5::CoreAddons KF5::ConfigWidgets KF5::GlobalAccel KF5::GlobalAccelPrivate KF5::I18n KF5::Notifications KF5::Package KF5::Plasma KF5::WindowSystem KF5::QuickAddons KDecoration2::KDecoration KDecoration2::KDecoration2Private PW::KScreenLocker ) set(kwin_XLIB_LIBS ${X11_X11_LIB} ${X11_ICE_LIB} ${X11_SM_LIB} ) 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::ICCCM ) 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} ${UDEV_LIBS} Libinput::Libinput ) 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}) kf5_add_kdeinit_executable(kwin_x11 main_x11.cpp) target_link_libraries(kdeinit_kwin_x11 kwin KF5::Crash Qt5::X11Extras) 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 tabletmodemanager.cpp main_wayland.cpp) target_link_libraries(kwin_wayland kwin) if (HAVE_LIBCAP) target_link_libraries(kwin_wayland ${Libcap_LIBRARIES}) endif() install(TARGETS kwin_wayland ${INSTALL_TARGETS_DEFAULT_ARGS} ) if (HAVE_LIBCAP) install( CODE "execute_process( COMMAND ${SETCAP_EXECUTABLE} CAP_SYS_NICE=+ep \$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/kwin_wayland)" ) endif() add_subdirectory(platformsupport) add_subdirectory(plugins) ########### install files ############### install( FILES kwin.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}.kcfg ) install( FILES colorcorrection/colorcorrect_settings.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}_colorcorrect.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.ColorCorrect.xml org.kde.kwin.Effects.xml org.kde.KWin.VirtualDesktopManager.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(packageplugins) if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() if (KF5DocTools_FOUND) add_subdirectory(doc) endif() add_subdirectory(kconf_update) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) include(CMakePackageConfigHelpers) set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KWinDBusInterface") 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/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index c14010ea9..501ef2d09 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -1,655 +1,658 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 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 . *********************************************************************/ // own #include "tabboxhandler.h" #include #include #include "xcbutils.h" // tabbox #include "clientmodel.h" #include "desktopmodel.h" #include "tabboxconfig.h" #include "thumbnailitem.h" #include "scripting/scripting.h" #include "switcheritem.h" #include "tabbox_logging.h" // Qt #include #include #include #include #include #include #include #include #include #include +#include // KDE #include #include #include #include namespace KWin { namespace TabBox { class TabBoxHandlerPrivate { public: TabBoxHandlerPrivate(TabBoxHandler *q); ~TabBoxHandlerPrivate(); /** * Updates the current highlight window state */ void updateHighlightWindows(); /** * Ends window highlighting */ void endHighlightWindows(bool abort = false); void show(); QQuickWindow *window() const; SwitcherItem *switcherItem() const; ClientModel* clientModel() const; DesktopModel* desktopModel() const; TabBoxHandler *q; // public pointer // members TabBoxConfig config; QScopedPointer m_qmlContext; QScopedPointer m_qmlComponent; QObject *m_mainItem; QMap m_clientTabBoxes; QMap m_desktopTabBoxes; ClientModel* m_clientModel; DesktopModel* m_desktopModel; QModelIndex index; /** * Indicates if the tabbox is shown. */ bool isShown; TabBoxClient *lastRaisedClient, *lastRaisedClientSucc; int wheelAngleDelta = 0; private: QObject *createSwitcherItem(bool desktopMode); }; TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q) : m_qmlContext() , m_qmlComponent() , m_mainItem(nullptr) { this->q = q; isShown = false; lastRaisedClient = nullptr; lastRaisedClientSucc = nullptr; config = TabBoxConfig(); m_clientModel = new ClientModel(q); m_desktopModel = new DesktopModel(q); } TabBoxHandlerPrivate::~TabBoxHandlerPrivate() { for (auto it = m_clientTabBoxes.constBegin(); it != m_clientTabBoxes.constEnd(); ++it) { delete it.value(); } for (auto it = m_desktopTabBoxes.constBegin(); it != m_desktopTabBoxes.constEnd(); ++it) { delete it.value(); } } QQuickWindow *TabBoxHandlerPrivate::window() const { if (!m_mainItem) { return nullptr; } if (QQuickWindow *w = qobject_cast(m_mainItem)) { return w; } return m_mainItem->findChild(); } #ifndef KWIN_UNIT_TEST SwitcherItem *TabBoxHandlerPrivate::switcherItem() const { if (!m_mainItem) { return nullptr; } if (SwitcherItem *i = qobject_cast(m_mainItem)) { return i; } else if (QQuickWindow *w = qobject_cast(m_mainItem)) { return w->contentItem()->findChild(); } return m_mainItem->findChild(); } #endif ClientModel* TabBoxHandlerPrivate::clientModel() const { return m_clientModel; } DesktopModel* TabBoxHandlerPrivate::desktopModel() const { return m_desktopModel; } void TabBoxHandlerPrivate::updateHighlightWindows() { if (!isShown || config.tabBoxMode() != TabBoxConfig::ClientTabBox) return; TabBoxClient *currentClient = q->client(index); QWindow *w = window(); if (q->isKWinCompositing()) { if (lastRaisedClient) q->elevateClient(lastRaisedClient, w, false); lastRaisedClient = currentClient; if (currentClient) q->elevateClient(currentClient, w, true); } else { if (lastRaisedClient) { q->shadeClient(lastRaisedClient, true); if (lastRaisedClientSucc) q->restack(lastRaisedClient, lastRaisedClientSucc); // TODO lastRaisedClient->setMinimized( lastRaisedClientWasMinimized ); } lastRaisedClient = currentClient; if (lastRaisedClient) { q->shadeClient(lastRaisedClient, false); // TODO if ( (lastRaisedClientWasMinimized = lastRaisedClient->isMinimized()) ) // lastRaisedClient->setMinimized( false ); TabBoxClientList order = q->stackingOrder(); int succIdx = order.count() + 1; for (int i=0; iraiseClient(lastRaisedClient); } } if (config.isShowTabBox() && w) { q->highlightWindows(currentClient, w); } else { q->highlightWindows(currentClient); } } void TabBoxHandlerPrivate::endHighlightWindows(bool abort) { TabBoxClient *currentClient = q->client(index); if (config.isHighlightWindows() && q->isKWinCompositing()) { foreach (const QWeakPointer &clientPointer, q->stackingOrder()) { if (QSharedPointer client = clientPointer.toStrongRef()) if (client != currentClient) // to not mess up with wanted ShadeActive/ShadeHover state q->shadeClient(client.data(), true); } } QWindow *w = window(); if (currentClient) q->elevateClient(currentClient, w, false); if (abort && lastRaisedClient && lastRaisedClientSucc) q->restack(lastRaisedClient, lastRaisedClientSucc); lastRaisedClient = nullptr; lastRaisedClientSucc = nullptr; // highlight windows q->highlightWindows(); } #ifndef KWIN_UNIT_TEST QObject *TabBoxHandlerPrivate::createSwitcherItem(bool desktopMode) { // first try look'n'feel package QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/look-and-feel/%1/contents/%2") .arg(config.layoutName()) .arg(desktopMode ? QStringLiteral("desktopswitcher/DesktopSwitcher.qml") : QStringLiteral("windowswitcher/WindowSwitcher.qml"))); if (file.isNull()) { const QString folderName = QLatin1String(KWIN_NAME) + (desktopMode ? QLatin1String("/desktoptabbox/") : QLatin1String("/tabbox/")); auto findSwitcher = [this, desktopMode, folderName] { const QString type = desktopMode ? QStringLiteral("KWin/DesktopSwitcher") : QStringLiteral("KWin/WindowSwitcher"); auto offers = KPackage::PackageLoader::self()->findPackages(type, folderName, [this] (const KPluginMetaData &data) { return data.pluginId().compare(config.layoutName(), Qt::CaseInsensitive) == 0; } ); if (offers.isEmpty()) { // load default offers = KPackage::PackageLoader::self()->findPackages(type, folderName, [] (const KPluginMetaData &data) { return data.pluginId().compare(QStringLiteral("informative"), Qt::CaseInsensitive) == 0; } ); if (offers.isEmpty()) { qCDebug(KWIN_TABBOX) << "could not find default window switcher layout"; return KPluginMetaData(); } } return offers.first(); }; auto service = findSwitcher(); if (!service.isValid()) { return nullptr; } if (service.value(QStringLiteral("X-Plasma-API")) != QLatin1String("declarativeappletscript")) { qCDebug(KWIN_TABBOX) << "Window Switcher Layout is no declarativeappletscript"; return nullptr; } auto findScriptFile = [service, folderName] { const QString pluginName = service.pluginId(); const QString scriptName = service.value(QStringLiteral("X-Plasma-MainScript")); return QStandardPaths::locate(QStandardPaths::GenericDataLocation, folderName + pluginName + QLatin1String("/contents/") + scriptName); }; file = findScriptFile(); } if (file.isNull()) { qCDebug(KWIN_TABBOX) << "Could not find QML file for window switcher"; return nullptr; } m_qmlComponent->loadUrl(QUrl::fromLocalFile(file)); if (m_qmlComponent->isError()) { qCDebug(KWIN_TABBOX) << "Component failed to load: " << m_qmlComponent->errors(); QStringList args; args << QStringLiteral("--passivepopup") << i18n("The Window Switcher installation is broken, resources are missing.\n" "Contact your distribution about this.") << QStringLiteral("20"); KProcess::startDetached(QStringLiteral("kdialog"), args); } else { QObject *object = m_qmlComponent->create(m_qmlContext.data()); if (desktopMode) { m_desktopTabBoxes.insert(config.layoutName(), object); } else { m_clientTabBoxes.insert(config.layoutName(), object); } return object; } return nullptr; } #endif void TabBoxHandlerPrivate::show() { #ifndef KWIN_UNIT_TEST if (m_qmlContext.isNull()) { qmlRegisterType("org.kde.kwin", 2, 0, "Switcher"); m_qmlContext.reset(new QQmlContext(Scripting::self()->qmlEngine())); } if (m_qmlComponent.isNull()) { m_qmlComponent.reset(new QQmlComponent(Scripting::self()->qmlEngine())); } const bool desktopMode = (config.tabBoxMode() == TabBoxConfig::DesktopTabBox); auto findMainItem = [this](const QMap &tabBoxes) -> QObject* { auto it = tabBoxes.constFind(config.layoutName()); if (it != tabBoxes.constEnd()) { return it.value(); } return nullptr; }; m_mainItem = nullptr; m_mainItem = desktopMode ? findMainItem(m_desktopTabBoxes) : findMainItem(m_clientTabBoxes); if (!m_mainItem) { m_mainItem = createSwitcherItem(desktopMode); if (!m_mainItem) { return; } } if (SwitcherItem *item = switcherItem()) { // In case the model isn't yet set (see below), index will be reset and therefore we // need to save the current index row (https://bugs.kde.org/show_bug.cgi?id=333511). int indexRow = index.row(); if (!item->model()) { QAbstractItemModel *model = nullptr; if (desktopMode) { model = desktopModel(); } else { model = clientModel(); } item->setModel(model); } item->setAllDesktops(config.clientDesktopMode() == TabBoxConfig::AllDesktopsClients); item->setCurrentIndex(indexRow); item->setNoModifierGrab(q->noModifierGrab()); // everything is prepared, so let's make the whole thing visible item->setVisible(true); } if (QWindow *w = window()) { wheelAngleDelta = 0; w->installEventFilter(q); + // pretend to activate the window to enable accessibility notifications + QWindowSystemInterface::handleWindowActivated(w, Qt::TabFocusReason); } #endif } /*********************************************** * TabBoxHandler ***********************************************/ TabBoxHandler::TabBoxHandler(QObject *parent) : QObject(parent) { KWin::TabBox::tabBox = this; d = new TabBoxHandlerPrivate(this); } TabBoxHandler::~TabBoxHandler() { delete d; } const KWin::TabBox::TabBoxConfig& TabBoxHandler::config() const { return d->config; } void TabBoxHandler::setConfig(const TabBoxConfig& config) { d->config = config; emit configChanged(); } void TabBoxHandler::show() { d->isShown = true; d->lastRaisedClient = nullptr; d->lastRaisedClientSucc = nullptr; if (d->config.isShowTabBox()) { d->show(); } if (d->config.isHighlightWindows()) { if (kwinApp()->x11Connection()) { Xcb::sync(); } // TODO this should be // QMetaObject::invokeMethod(this, "initHighlightWindows", Qt::QueuedConnection); // but we somehow need to cross > 1 event cycle (likely because of queued invocation in the effects) // to ensure the EffectWindow is present when updateHighlightWindows, thus elevating the window/tabbox QTimer::singleShot(1, this, SLOT(initHighlightWindows())); } } void TabBoxHandler::initHighlightWindows() { if (isKWinCompositing()) { foreach (const QWeakPointer &clientPointer, stackingOrder()) { if (QSharedPointer client = clientPointer.toStrongRef()) shadeClient(client.data(), false); } } d->updateHighlightWindows(); } void TabBoxHandler::hide(bool abort) { d->isShown = false; if (d->config.isHighlightWindows()) { d->endHighlightWindows(abort); } #ifndef KWIN_UNIT_TEST if (SwitcherItem *item = d->switcherItem()) { item->setVisible(false); } #endif if (QQuickWindow *w = d->window()) { w->hide(); w->destroy(); } d->m_mainItem = nullptr; } QModelIndex TabBoxHandler::nextPrev(bool forward) const { QModelIndex ret; QAbstractItemModel* model; switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: model = d->clientModel(); break; case TabBoxConfig::DesktopTabBox: model = d->desktopModel(); break; } if (forward) { int column = d->index.column() + 1; int row = d->index.row(); if (column == model->columnCount()) { column = 0; row++; if (row == model->rowCount()) row = 0; } ret = model->index(row, column); if (!ret.isValid()) ret = model->index(0, 0); } else { int column = d->index.column() - 1; int row = d->index.row(); if (column < 0) { column = model->columnCount() - 1; row--; if (row < 0) row = model->rowCount() - 1; } ret = model->index(row, column); if (!ret.isValid()) { row = model->rowCount() - 1; for (int i = model->columnCount() - 1; i >= 0; i--) { ret = model->index(row, i); if (ret.isValid()) break; } } } if (ret.isValid()) return ret; else return d->index; } QModelIndex TabBoxHandler::desktopIndex(int desktop) const { if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) return QModelIndex(); return d->desktopModel()->desktopIndex(desktop); } QList< int > TabBoxHandler::desktopList() const { if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) return QList< int >(); return d->desktopModel()->desktopList(); } int TabBoxHandler::desktop(const QModelIndex& index) const { if (!index.isValid() || (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox)) return -1; QVariant ret = d->desktopModel()->data(index, DesktopModel::DesktopRole); if (ret.isValid()) return ret.toInt(); else return -1; } void TabBoxHandler::setCurrentIndex(const QModelIndex& index) { if (d->index == index) { return; } if (!index.isValid()) { return; } d->index = index; if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) { if (d->config.isHighlightWindows()) { d->updateHighlightWindows(); } } emit selectedIndexChanged(); } const QModelIndex& TabBoxHandler::currentIndex() const { return d->index; } void TabBoxHandler::grabbedKeyEvent(QKeyEvent* event) const { if (!d->m_mainItem || !d->window()) { return; } const QList items = d->window()->contentItem()->findChildren(QString(), Qt::FindDirectChildrenOnly); for (QQuickItem *item : items) { d->window()->sendEvent(item, event); if (event->isAccepted()) { break; } } } bool TabBoxHandler::containsPos(const QPoint& pos) const { if (!d->m_mainItem) { return false; } QWindow *w = d->window(); if (w) { return w->geometry().contains(pos); } return false; } QModelIndex TabBoxHandler::index(QWeakPointer client) const { return d->clientModel()->index(client); } TabBoxClientList TabBoxHandler::clientList() const { if (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox) return TabBoxClientList(); return d->clientModel()->clientList(); } TabBoxClient* TabBoxHandler::client(const QModelIndex& index) const { if ((!index.isValid()) || (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox)) return nullptr; TabBoxClient* c = static_cast< TabBoxClient* >( d->clientModel()->data(index, ClientModel::ClientRole).value()); return c; } void TabBoxHandler::createModel(bool partialReset) { switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: { d->clientModel()->createClientList(partialReset); // TODO: C++11 use lambda function bool lastRaised = false; bool lastRaisedSucc = false; foreach (const QWeakPointer &clientPointer, stackingOrder()) { QSharedPointer client = clientPointer.toStrongRef(); if (!client) { continue; } if (client.data() == d->lastRaisedClient) { lastRaised = true; } if (client.data() == d->lastRaisedClientSucc) { lastRaisedSucc = true; } } if (d->lastRaisedClient && !lastRaised) d->lastRaisedClient = nullptr; if (d->lastRaisedClientSucc && !lastRaisedSucc) d->lastRaisedClientSucc = nullptr; break; } case TabBoxConfig::DesktopTabBox: d->desktopModel()->createDesktopList(); break; } } QModelIndex TabBoxHandler::first() const { QAbstractItemModel* model; switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: model = d->clientModel(); break; case TabBoxConfig::DesktopTabBox: model = d->desktopModel(); break; } return model->index(0, 0); } bool TabBoxHandler::eventFilter(QObject *watched, QEvent *e) { if (e->type() == QEvent::Wheel && watched == d->window()) { QWheelEvent *event = static_cast(e); // On x11 the delta for vertical scrolling might also be on X for whatever reason const int delta = qAbs(event->angleDelta().x()) > qAbs(event->angleDelta().y()) ? event->angleDelta().x() : event->angleDelta().y(); d->wheelAngleDelta += delta; while (d->wheelAngleDelta <= -120) { d->wheelAngleDelta += 120; const QModelIndex index = nextPrev(true); if (index.isValid()) { setCurrentIndex(index); } } while (d->wheelAngleDelta >= 120) { d->wheelAngleDelta -= 120; const QModelIndex index = nextPrev(false); if (index.isValid()) { setCurrentIndex(index); } } return true; } // pass on return QObject::eventFilter(watched, e); } TabBoxHandler* tabBox = nullptr; TabBoxClient::TabBoxClient() { } TabBoxClient::~TabBoxClient() { } } // namespace TabBox } // namespace KWin