diff --git a/CMakeLists.txt b/CMakeLists.txt index d9632589c..4d2c469dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,638 +1,637 @@ 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.5.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) 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") 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() 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 platform.cpp shell_client.cpp wayland_server.cpp wayland_cursor_theme.cpp ) 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/device.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::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 KF5::Crash) 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/composite.cpp b/composite.cpp index 7b0a166f4..77163635d 100644 --- a/composite.cpp +++ b/composite.cpp @@ -1,1330 +1,1329 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 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 "composite.h" #include "dbusinterface.h" #include "utils.h" #include #include "workspace.h" #include "client.h" #include "unmanaged.h" #include "deleted.h" #include "effects.h" #include "overlaywindow.h" #include "scene.h" #include "scene_xrender.h" #include "scene_opengl.h" #include "scene_qpainter.h" #include "screens.h" #include "shadow.h" #include "useractions.h" -#include "compositingprefs.h" #include "xcbutils.h" #include "platform.h" #include "shell_client.h" #include "wayland_server.h" #include "decorations/decoratedclient.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(KWin::Compositor::SuspendReason) namespace KWin { extern int currentRefreshRate(); CompositorSelectionOwner::CompositorSelectionOwner(const char *selection) : KSelectionOwner(selection, connection(), rootWindow()), owning(false) { connect (this, SIGNAL(lostOwnership()), SLOT(looseOwnership())); } void CompositorSelectionOwner::looseOwnership() { owning = false; } KWIN_SINGLETON_FACTORY_VARIABLE(Compositor, s_compositor) static inline qint64 milliToNano(int milli) { return qint64(milli) * 1000 * 1000; } static inline qint64 nanoToMilli(int nano) { return nano / (1000*1000); } Compositor::Compositor(QObject* workspace) : QObject(workspace) , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend) , cm_selection(NULL) , vBlankInterval(0) , fpsInterval(0) , m_xrrRefreshRate(0) , forceUnredirectCheck(false) , m_finishing(false) , m_timeSinceLastVBlank(0) , m_scene(NULL) , m_bufferSwapPending(false) , m_composeAtSwapCompletion(false) { qRegisterMetaType("Compositor::SuspendReason"); connect(&unredirectTimer, SIGNAL(timeout()), SLOT(delayedCheckUnredirect())); connect(&compositeResetTimer, SIGNAL(timeout()), SLOT(restart())); connect(options, &Options::configChanged, this, &Compositor::slotConfigChanged); connect(options, SIGNAL(unredirectFullscreenChanged()), SLOT(delayedCheckUnredirect())); unredirectTimer.setSingleShot(true); compositeResetTimer.setSingleShot(true); nextPaintReference.invalidate(); // Initialize the timer // 2 sec which should be enough to restart the compositor static const int compositorLostMessageDelay = 2000; m_releaseSelectionTimer.setSingleShot(true); m_releaseSelectionTimer.setInterval(compositorLostMessageDelay); connect(&m_releaseSelectionTimer, SIGNAL(timeout()), SLOT(releaseCompositorSelection())); m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); m_unusedSupportPropertyTimer.setSingleShot(true); connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties())); // delay the call to setup by one event cycle // The ctor of this class is invoked from the Workspace ctor, that means before // Workspace is completely constructed, so calling Workspace::self() would result // in undefined behavior. This is fixed by using a delayed invocation. if (kwinApp()->platform()->isReady()) { QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } connect(kwinApp()->platform(), &Platform::readyChanged, this, [this] (bool ready) { if (ready) { setup(); } else { finish(); } }, Qt::QueuedConnection ); connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, this, [this] { delete cm_selection; cm_selection = nullptr; } ); // register DBus new CompositorDBusInterface(this); } Compositor::~Compositor() { emit aboutToDestroy(); finish(); deleteUnusedSupportProperties(); delete cm_selection; s_compositor = NULL; } void Compositor::setup() { if (hasScene()) return; if (m_suspended) { QStringList reasons; if (m_suspended & UserSuspend) { reasons << QStringLiteral("Disabled by User"); } if (m_suspended & BlockRuleSuspend) { reasons << QStringLiteral("Disabled by Window"); } if (m_suspended & ScriptSuspend) { reasons << QStringLiteral("Disabled by Script"); } qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons; return; - } else if (!CompositingPrefs::compositingPossible()) { + } else if (!kwinApp()->platform()->compositingPossible()) { qCCritical(KWIN_CORE) << "Compositing is not possible"; return; } m_starting = true; if (!options->isCompositingInitialized()) { options->reloadCompositingSettings(true); slotCompositingOptionsInitialized(); } else { slotCompositingOptionsInitialized(); } } extern int screen_number; // main.cpp extern bool is_multihead; void Compositor::slotCompositingOptionsInitialized() { claimCompositorSelection(); // There might still be a deleted around, needs to be cleared before creating the scene (BUG 333275) if (Workspace::self()) { while (!Workspace::self()->deletedList().isEmpty()) { Workspace::self()->deletedList().first()->discard(); } } switch(options->compositingMode()) { case OpenGLCompositing: { qCDebug(KWIN_CORE) << "Initializing OpenGL compositing"; // Some broken drivers crash on glXQuery() so to prevent constant KWin crashes: KSharedConfigPtr unsafeConfigPtr = kwinApp()->config(); KConfigGroup unsafeConfig(unsafeConfigPtr, "Compositing"); const QString openGLIsUnsafe = QLatin1String("OpenGLIsUnsafe") + (is_multihead ? QString::number(screen_number) : QString()); if (unsafeConfig.readEntry(openGLIsUnsafe, false)) qCWarning(KWIN_CORE) << "KWin has detected that your OpenGL library is unsafe to use"; else { unsafeConfig.writeEntry(openGLIsUnsafe, true); unsafeConfig.sync(); - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL && !kwinApp()->shouldUseWaylandForCompositing() && !CompositingPrefs::hasGlx()) { + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL && !kwinApp()->shouldUseWaylandForCompositing() && !Xcb::Extensions::self()->hasGlx()) { unsafeConfig.writeEntry(openGLIsUnsafe, false); unsafeConfig.sync(); qCDebug(KWIN_CORE) << "No glx extensions available"; break; } m_scene = SceneOpenGL::createScene(this); // TODO: Add 30 second delay to protect against screen freezes as well unsafeConfig.writeEntry(openGLIsUnsafe, false); unsafeConfig.sync(); if (m_scene && !m_scene->initFailed()) { connect(static_cast(m_scene), &SceneOpenGL::resetCompositing, this, &Compositor::restart); break; // --> } delete m_scene; m_scene = NULL; } // Do not Fall back to XRender - it causes problems when selfcheck fails during startup, but works later on break; } #ifdef KWIN_HAVE_XRENDER_COMPOSITING case XRenderCompositing: qCDebug(KWIN_CORE) << "Initializing XRender compositing"; m_scene = SceneXrender::createScene(this); break; #endif case QPainterCompositing: qCDebug(KWIN_CORE) << "Initializing QPainter compositing"; m_scene = SceneQPainter::createScene(this); break; default: qCDebug(KWIN_CORE) << "No compositing enabled"; m_starting = false; if (cm_selection) { cm_selection->owning = false; cm_selection->release(); } if (kwinApp()->platform()->requiresCompositing()) { qCCritical(KWIN_CORE) << "The used windowing system requires compositing"; qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken"; qApp->quit(); } return; } if (m_scene == NULL || m_scene->initFailed()) { qCCritical(KWIN_CORE) << "Failed to initialize compositing, compositing disabled"; delete m_scene; m_scene = NULL; m_starting = false; if (cm_selection) { cm_selection->owning = false; cm_selection->release(); } if (kwinApp()->platform()->requiresCompositing()) { qCCritical(KWIN_CORE) << "The used windowing system requires compositing"; qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken"; qApp->quit(); } return; } emit sceneCreated(); if (Workspace::self()) { startupWithWorkspace(); } else { connect(kwinApp(), &Application::workspaceCreated, this, &Compositor::startupWithWorkspace); } } void Compositor::claimCompositorSelection() { if (!cm_selection && kwinApp()->x11Connection()) { char selection_name[ 100 ]; sprintf(selection_name, "_NET_WM_CM_S%d", Application::x11ScreenNumber()); cm_selection = new CompositorSelectionOwner(selection_name); connect(cm_selection, SIGNAL(lostOwnership()), SLOT(finish())); } if (!cm_selection) // no X11 yet return; if (!cm_selection->owning) { cm_selection->claim(true); // force claiming cm_selection->owning = true; } } void Compositor::startupWithWorkspace() { if (!m_starting) { return; } Q_ASSERT(m_scene); claimCompositorSelection(); m_xrrRefreshRate = KWin::currentRefreshRate(); fpsInterval = options->maxFpsInterval(); if (m_scene->syncsToVBlank()) { // if we do vsync, set the fps to the next multiple of the vblank rate vBlankInterval = milliToNano(1000) / m_xrrRefreshRate; fpsInterval = qMax((fpsInterval / vBlankInterval) * vBlankInterval, vBlankInterval); } else vBlankInterval = milliToNano(1); // no sync - DO NOT set "0", would cause div-by-zero segfaults. m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" - we don't have even a slight idea when the first vsync will occur scheduleRepaint(); xcb_composite_redirect_subwindows(connection(), rootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); new EffectsHandlerImpl(this, m_scene); // sets also the 'effects' pointer connect(Workspace::self(), &Workspace::deletedRemoved, m_scene, &Scene::windowDeleted); connect(effects, SIGNAL(screenGeometryChanged(QSize)), SLOT(addRepaintFull())); addRepaintFull(); foreach (Client * c, Workspace::self()->clientList()) { c->setupCompositing(); c->getShadow(); } foreach (Client * c, Workspace::self()->desktopList()) c->setupCompositing(); foreach (Unmanaged * c, Workspace::self()->unmanagedList()) { c->setupCompositing(); c->getShadow(); } emit compositingToggled(true); m_starting = false; if (m_releaseSelectionTimer.isActive()) { m_releaseSelectionTimer.stop(); } // render at least once performCompositing(); } void Compositor::scheduleRepaint() { if (!compositeTimer.isActive()) setCompositeTimer(); } void Compositor::finish() { if (!hasScene()) return; m_finishing = true; m_releaseSelectionTimer.start(); if (Workspace::self()) { foreach (Client * c, Workspace::self()->clientList()) m_scene->windowClosed(c, NULL); foreach (Client * c, Workspace::self()->desktopList()) m_scene->windowClosed(c, NULL); foreach (Unmanaged * c, Workspace::self()->unmanagedList()) m_scene->windowClosed(c, NULL); foreach (Deleted * c, Workspace::self()->deletedList()) m_scene->windowDeleted(c); foreach (Client * c, Workspace::self()->clientList()) c->finishCompositing(); foreach (Client * c, Workspace::self()->desktopList()) c->finishCompositing(); foreach (Unmanaged * c, Workspace::self()->unmanagedList()) c->finishCompositing(); foreach (Deleted * c, Workspace::self()->deletedList()) c->finishCompositing(); xcb_composite_unredirect_subwindows(connection(), rootWindow(), XCB_COMPOSITE_REDIRECT_MANUAL); } delete effects; effects = NULL; delete m_scene; m_scene = NULL; compositeTimer.stop(); repaints_region = QRegion(); if (Workspace::self()) { for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) { // forward all opacity values to the frame in case there'll be other CM running if ((*it)->opacity() != 1.0) { NETWinInfo i(connection(), (*it)->frameId(), rootWindow(), 0, 0); i.setOpacity(static_cast< unsigned long >((*it)->opacity() * 0xffffffff)); } } // discard all Deleted windows (#152914) while (!Workspace::self()->deletedList().isEmpty()) Workspace::self()->deletedList().first()->discard(); } m_finishing = false; emit compositingToggled(false); } void Compositor::releaseCompositorSelection() { if (hasScene() && !m_finishing) { // compositor is up and running again, no need to release the selection return; } if (m_starting) { // currently still starting the compositor, it might fail, so restart the timer to test again m_releaseSelectionTimer.start(); return; } if (m_finishing) { // still shutting down, a restart might follow, so restart the timer to test again m_releaseSelectionTimer.start(); return; } qCDebug(KWIN_CORE) << "Releasing compositor selection"; if (cm_selection) { cm_selection->owning = false; cm_selection->release(); } } void Compositor::keepSupportProperty(xcb_atom_t atom) { m_unusedSupportProperties.removeAll(atom); } void Compositor::removeSupportProperty(xcb_atom_t atom) { m_unusedSupportProperties << atom; m_unusedSupportPropertyTimer.start(); } void Compositor::deleteUnusedSupportProperties() { if (m_starting) { // currently still starting the compositor m_unusedSupportPropertyTimer.start(); return; } if (m_finishing) { // still shutting down, a restart might follow m_unusedSupportPropertyTimer.start(); return; } if (kwinApp()->x11Connection()) { foreach (const xcb_atom_t &atom, m_unusedSupportProperties) { // remove property from root window xcb_delete_property(connection(), rootWindow(), atom); } } } // OpenGL self-check failed, fallback to XRender void Compositor::fallbackToXRenderCompositing() { finish(); KConfigGroup config(kwinApp()->config(), "Compositing"); config.writeEntry("Backend", "XRender"); config.sync(); options->setCompositingMode(XRenderCompositing); setup(); } void Compositor::slotConfigChanged() { if (!m_suspended) { setup(); if (effects) // setupCompositing() may fail effects->reconfigure(); addRepaintFull(); } else finish(); } void Compositor::slotReinitialize() { // Reparse config. Config options will be reloaded by setup() kwinApp()->config()->reparseConfiguration(); // Restart compositing finish(); // resume compositing if suspended m_suspended = NoReasonSuspend; options->setCompositingInitialized(false); setup(); if (effects) { // setup() may fail effects->reconfigure(); } } // for the shortcut void Compositor::slotToggleCompositing() { if (kwinApp()->platform()->requiresCompositing()) { // we are not allowed to turn on/off compositing return; } if (m_suspended) { // direct user call; clear all bits resume(AllReasonSuspend); } else { // but only set the user one (sufficient to suspend) suspend(UserSuspend); } } void Compositor::updateCompositeBlocking() { updateCompositeBlocking(NULL); } void Compositor::updateCompositeBlocking(Client *c) { if (kwinApp()->platform()->requiresCompositing()) { return; } if (c) { // if c == 0 we just check if we can resume if (c->isBlockingCompositing()) { if (!(m_suspended & BlockRuleSuspend)) // do NOT attempt to call suspend(true); from within the eventchain! QMetaObject::invokeMethod(this, "suspend", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend)); } } else if (m_suspended & BlockRuleSuspend) { // lost a client and we're blocked - can we resume? bool resume = true; for (ClientList::ConstIterator it = Workspace::self()->clientList().constBegin(); it != Workspace::self()->clientList().constEnd(); ++it) { if ((*it)->isBlockingCompositing()) { resume = false; break; } } if (resume) { // do NOT attempt to call suspend(false); from within the eventchain! QMetaObject::invokeMethod(this, "resume", Qt::QueuedConnection, Q_ARG(Compositor::SuspendReason, BlockRuleSuspend)); } } } void Compositor::suspend(Compositor::SuspendReason reason) { if (kwinApp()->platform()->requiresCompositing()) { return; } Q_ASSERT(reason != NoReasonSuspend); m_suspended |= reason; if (reason & KWin::Compositor::ScriptSuspend) { // when disabled show a shortcut how the user can get back compositing const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild(QStringLiteral("Suspend Compositing"))); if (!shortcuts.isEmpty()) { // display notification only if there is the shortcut const QString message = i18n("Desktop effects have been suspended by another application.
" "You can resume using the '%1' shortcut.", shortcuts.first().toString(QKeySequence::NativeText)); KNotification::event(QStringLiteral("compositingsuspendeddbus"), message); } } finish(); } void Compositor::resume(Compositor::SuspendReason reason) { Q_ASSERT(reason != NoReasonSuspend); m_suspended &= ~reason; setup(); // signal "toggled" is eventually emitted from within setup } void Compositor::restart() { if (hasScene()) { finish(); QTimer::singleShot(0, this, SLOT(setup())); } } void Compositor::addRepaint(int x, int y, int w, int h) { if (!hasScene()) return; repaints_region += QRegion(x, y, w, h); scheduleRepaint(); } void Compositor::addRepaint(const QRect& r) { if (!hasScene()) return; repaints_region += r; scheduleRepaint(); } void Compositor::addRepaint(const QRegion& r) { if (!hasScene()) return; repaints_region += r; scheduleRepaint(); } void Compositor::addRepaintFull() { if (!hasScene()) return; const QSize &s = screens()->size(); repaints_region = QRegion(0, 0, s.width(), s.height()); scheduleRepaint(); } void Compositor::timerEvent(QTimerEvent *te) { if (te->timerId() == compositeTimer.timerId()) { performCompositing(); } else QObject::timerEvent(te); } void Compositor::aboutToSwapBuffers() { assert(!m_bufferSwapPending); m_bufferSwapPending = true; } void Compositor::bufferSwapComplete() { assert(m_bufferSwapPending); m_bufferSwapPending = false; if (m_composeAtSwapCompletion) { m_composeAtSwapCompletion = false; performCompositing(); } } void Compositor::performCompositing() { if (m_scene->usesOverlayWindow() && !isOverlayWindowVisible()) return; // nothing is visible anyway // If a buffer swap is still pending, we return to the event loop and // continue processing events until the swap has completed. if (m_bufferSwapPending) { m_composeAtSwapCompletion = true; compositeTimer.stop(); return; } // If outputs are disabled, we return to the event loop and // continue processing events until the outputs are enabled again if (!kwinApp()->platform()->areOutputsEnabled()) { compositeTimer.stop(); return; } // Create a list of all windows in the stacking order ToplevelList windows = Workspace::self()->xStackingOrder(); ToplevelList damaged; // Reset the damage state of each window and fetch the damage region // without waiting for a reply foreach (Toplevel *win, windows) { if (win->resetAndFetchDamage()) damaged << win; } if (damaged.count() > 0) { m_scene->triggerFence(); xcb_flush(connection()); } // Move elevated windows to the top of the stacking order foreach (EffectWindow *c, static_cast(effects)->elevatedWindows()) { Toplevel* t = static_cast< EffectWindowImpl* >(c)->window(); windows.removeAll(t); windows.append(t); } // Get the replies foreach (Toplevel *win, damaged) { // Discard the cached lanczos texture if (win->effectWindow()) { const QVariant texture = win->effectWindow()->data(LanczosCacheRole); if (texture.isValid()) { delete static_cast(texture.value()); win->effectWindow()->setData(LanczosCacheRole, QVariant()); } } win->getDamageRegionReply(); } if (repaints_region.isEmpty() && !windowRepaintsPending()) { m_scene->idle(); m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" m_timeSinceStart += m_timeSinceLastVBlank; // Note: It would seem here we should undo suspended unredirect, but when scenes need // it for some reason, e.g. transformations or translucency, the next pass that does not // need this anymore and paints normally will also reset the suspended unredirect. // Otherwise the window would not be painted normally anyway. compositeTimer.stop(); return; } // skip windows that are not yet ready for being painted and if screen is locked skip windows that are // neither lockscreen nor inputmethod windows // TODO ? // this cannot be used so carelessly - needs protections against broken clients, the window // should not get focus before it's displayed, handle unredirected windows properly and so on. foreach (Toplevel *t, windows) { if (!t->readyForPainting()) { windows.removeAll(t); } if (waylandServer() && waylandServer()->isScreenLocked()) { if(!t->isLockScreen() && !t->isInputMethod()) { windows.removeAll(t); } } } QRegion repaints = repaints_region; // clear all repaints, so that post-pass can add repaints for the next repaint repaints_region = QRegion(); m_timeSinceLastVBlank = m_scene->paint(repaints, windows); m_timeSinceStart += m_timeSinceLastVBlank; if (waylandServer()) { for (Toplevel *win : damaged) { if (auto surface = win->surface()) { surface->frameRendered(m_timeSinceStart); } } } compositeTimer.stop(); // stop here to ensure *we* cause the next repaint schedule - not some effect through m_scene->paint() // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle() // is called the next time. If there would be nothing pending, it will not restart the timer and // scheduleRepaint() would restart it again somewhen later, called from functions that // would again add something pending. if (m_bufferSwapPending && m_scene->syncsToVBlank()) { m_composeAtSwapCompletion = true; } else { scheduleRepaint(); } } bool Compositor::windowRepaintsPending() const { foreach (Toplevel * c, Workspace::self()->clientList()) if (!c->repaints().isEmpty()) return true; foreach (Toplevel * c, Workspace::self()->desktopList()) if (!c->repaints().isEmpty()) return true; foreach (Toplevel * c, Workspace::self()->unmanagedList()) if (!c->repaints().isEmpty()) return true; foreach (Toplevel * c, Workspace::self()->deletedList()) if (!c->repaints().isEmpty()) return true; if (auto w = waylandServer()) { const auto &clients = w->clients(); for (auto c : clients) { if (c->isShown(true) && !c->repaints().isEmpty()) { return true; } } const auto &internalClients = w->internalClients(); for (auto c : internalClients) { if (c->isShown(true) && !c->repaints().isEmpty()) { return true; } } } return false; } void Compositor::setCompositeResetTimer(int msecs) { compositeResetTimer.start(msecs); } void Compositor::setCompositeTimer() { if (!hasScene()) // should not really happen, but there may be e.g. some damage events still pending return; if (m_starting || !Workspace::self()) { return; } // Don't start the timer if we're waiting for a swap event if (m_bufferSwapPending && m_composeAtSwapCompletion) return; // Don't start the timer if all outputs are disabled if (!kwinApp()->platform()->areOutputsEnabled()) { return; } uint waitTime = 1; if (m_scene->blocksForRetrace()) { // TODO: make vBlankTime dynamic?! // It's required because glXWaitVideoSync will *likely* block a full frame if one enters // a retrace pass which can last a variable amount of time, depending on the actual screen // Now, my ooold 19" CRT can do such retrace so that 2ms are entirely sufficient, // while another ooold 15" TFT requires about 6ms qint64 padding = m_timeSinceLastVBlank; if (padding > fpsInterval) { // we're at low repaints or spent more time in painting than the user wanted to wait for that frame padding = vBlankInterval - (padding%vBlankInterval); // -> align to next vblank } else { // -> align to the next maxFps tick padding = ((vBlankInterval - padding%vBlankInterval) + (fpsInterval/vBlankInterval-1)*vBlankInterval); // "remaining time of the first vsync" + "time for the other vsyncs of the frame" } if (padding < options->vBlankTime()) { // we'll likely miss this frame waitTime = nanoToMilli(padding + vBlankInterval - options->vBlankTime()); // so we add one } else { waitTime = nanoToMilli(padding - options->vBlankTime()); } } else { // w/o blocking vsync we just jump to the next demanded tick if (fpsInterval > m_timeSinceLastVBlank) { waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank); if (!waitTime) { waitTime = 1; // will ensure we don't block out the eventloop - the system's just not faster ... } }/* else if (m_scene->syncsToVBlank() && m_timeSinceLastVBlank - fpsInterval < (vBlankInterval<<1)) { // NOTICE - "for later" ------------------------------------------------------------------ // It can happen that we push two frames within one refresh cycle. // Swapping will then block even with triple buffering when the GPU does not discard but // queues frames // now here's the mean part: if we take that as "OMG, we're late - next frame ASAP", // there'll immediately be 2 frames in the pipe, swapping will block, we think we're // late ... ewww // so instead we pad to the clock again and add 2ms safety to ensure the pipe is really // free // NOTICE: obviously m_timeSinceLastVBlank can be too big because we're too slow as well // So if this code was enabled, we'd needlessly half the framerate once more (15 instead of 30) waitTime = nanoToMilli(vBlankInterval - (m_timeSinceLastVBlank - fpsInterval)%vBlankInterval) + 2; }*/ else { waitTime = 1; // ... "0" would be sufficient, but the compositor isn't the WMs only task } } compositeTimer.start(qMin(waitTime, 250u), this); // force 4fps minimum } bool Compositor::isActive() { return !m_finishing && hasScene(); } void Compositor::checkUnredirect() { checkUnredirect(false); } // force is needed when the list of windows changes (e.g. a window goes away) void Compositor::checkUnredirect(bool force) { if (!hasScene() || !m_scene->overlayWindow() || m_scene->overlayWindow()->window() == None || !options->isUnredirectFullscreen()) return; if (force) forceUnredirectCheck = true; if (!unredirectTimer.isActive()) unredirectTimer.start(0); } void Compositor::delayedCheckUnredirect() { if (!hasScene() || !m_scene->overlayWindow() || m_scene->overlayWindow()->window() == None || !(options->isUnredirectFullscreen() || sender() == options)) return; ToplevelList list; bool changed = forceUnredirectCheck; foreach (Client * c, Workspace::self()->clientList()) list.append(c); foreach (Unmanaged * c, Workspace::self()->unmanagedList()) list.append(c); foreach (Toplevel * c, list) { if (c->updateUnredirectedState()) { changed = true; break; } } // no desktops, no Deleted ones if (!changed) return; forceUnredirectCheck = false; // Cut out parts from the overlay window where unredirected windows are, // so that they are actually visible. const QSize &s = screens()->size(); QRegion reg(0, 0, s.width(), s.height()); foreach (Toplevel * c, list) { if (c->unredirected()) reg -= c->geometry(); } m_scene->overlayWindow()->setShape(reg); addRepaint(reg); } bool Compositor::checkForOverlayWindow(WId w) const { if (!hasScene()) { // no scene, so it cannot be the overlay window return false; } if (!m_scene->overlayWindow()) { // no overlay window, it cannot be the overlay return false; } // and compare the window ID's return w == m_scene->overlayWindow()->window(); } WId Compositor::overlayWindow() const { if (!hasScene()) { return None; } if (!m_scene->overlayWindow()) { return None; } return m_scene->overlayWindow()->window(); } bool Compositor::isOverlayWindowVisible() const { if (!hasScene()) { return false; } if (!m_scene->overlayWindow()) { return false; } return m_scene->overlayWindow()->isVisible(); } void Compositor::setOverlayWindowVisibility(bool visible) { if (hasScene() && m_scene->overlayWindow()) { m_scene->overlayWindow()->setVisibility(visible); } } /***************************************************** * Workspace ****************************************************/ bool Workspace::compositing() const { return m_compositor && m_compositor->hasScene(); } //**************************************** // Toplevel //**************************************** bool Toplevel::setupCompositing() { if (!compositing()) return false; if (damage_handle != XCB_NONE) return false; if (kwinApp()->operationMode() == Application::OperationModeX11 && !surface()) { damage_handle = xcb_generate_id(connection()); xcb_damage_create(connection(), damage_handle, frameId(), XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); } damage_region = QRegion(0, 0, width(), height()); effect_window = new EffectWindowImpl(this); unredirect = false; Compositor::self()->checkUnredirect(true); Compositor::self()->scene()->windowAdded(this); // With unmanaged windows there is a race condition between the client painting the window // and us setting up damage tracking. If the client wins we won't get a damage event even // though the window has been painted. To avoid this we mark the whole window as damaged // and schedule a repaint immediately after creating the damage object. if (dynamic_cast(this)) addDamageFull(); return true; } void Toplevel::finishCompositing(ReleaseReason releaseReason) { if (kwinApp()->operationMode() == Application::OperationModeX11 && damage_handle == XCB_NONE) return; Compositor::self()->checkUnredirect(true); if (effect_window->window() == this) { // otherwise it's already passed to Deleted, don't free data discardWindowPixmap(); delete effect_window; } if (damage_handle != XCB_NONE && releaseReason != ReleaseReason::Destroyed) { xcb_damage_destroy(connection(), damage_handle); } damage_handle = XCB_NONE; damage_region = QRegion(); repaints_region = QRegion(); effect_window = NULL; } void Toplevel::discardWindowPixmap() { addDamageFull(); if (effectWindow() != NULL && effectWindow()->sceneWindow() != NULL) effectWindow()->sceneWindow()->pixmapDiscarded(); } void Toplevel::damageNotifyEvent() { m_isDamaged = true; // Note: The rect is supposed to specify the damage extents, // but we don't know it at this point. No one who connects // to this signal uses the rect however. emit damaged(this, QRect()); } bool Toplevel::compositing() const { return Workspace::self()->compositing(); } void Client::damageNotifyEvent() { if (syncRequest.isPending && isResize()) { emit damaged(this, QRect()); m_isDamaged = true; return; } if (!ready_for_painting) { // avoid "setReadyForPainting()" function calling overhead if (syncRequest.counter == XCB_NONE) { // cannot detect complete redraw, consider done now setReadyForPainting(); setupWindowManagementInterface(); } } Toplevel::damageNotifyEvent(); } bool Toplevel::resetAndFetchDamage() { if (!m_isDamaged) return false; if (damage_handle == XCB_NONE) { m_isDamaged = false; return true; } xcb_connection_t *conn = connection(); // Create a new region and copy the damage region to it, // resetting the damaged state. xcb_xfixes_region_t region = xcb_generate_id(conn); xcb_xfixes_create_region(conn, region, 0, 0); xcb_damage_subtract(conn, damage_handle, 0, region); // Send a fetch-region request and destroy the region m_regionCookie = xcb_xfixes_fetch_region_unchecked(conn, region); xcb_xfixes_destroy_region(conn, region); m_isDamaged = false; m_damageReplyPending = true; return m_damageReplyPending; } void Toplevel::getDamageRegionReply() { if (!m_damageReplyPending) return; m_damageReplyPending = false; // Get the fetch-region reply xcb_xfixes_fetch_region_reply_t *reply = xcb_xfixes_fetch_region_reply(connection(), m_regionCookie, 0); if (!reply) return; // Convert the reply to a QRegion int count = xcb_xfixes_fetch_region_rectangles_length(reply); QRegion region; if (count > 1 && count < 16) { xcb_rectangle_t *rects = xcb_xfixes_fetch_region_rectangles(reply); QVector qrects; qrects.reserve(count); for (int i = 0; i < count; i++) qrects << QRect(rects[i].x, rects[i].y, rects[i].width, rects[i].height); region.setRects(qrects.constData(), count); } else region += QRect(reply->extents.x, reply->extents.y, reply->extents.width, reply->extents.height); damage_region += region; repaints_region += region; free(reply); } void Toplevel::addDamageFull() { if (!compositing()) return; damage_region = rect(); repaints_region |= rect(); emit damaged(this, rect()); } void Toplevel::resetDamage() { damage_region = QRegion(); } void Toplevel::addRepaint(const QRect& r) { if (!compositing()) { return; } repaints_region += r; emit needsRepaint(); } void Toplevel::addRepaint(int x, int y, int w, int h) { QRect r(x, y, w, h); addRepaint(r); } void Toplevel::addRepaint(const QRegion& r) { if (!compositing()) { return; } repaints_region += r; emit needsRepaint(); } void Toplevel::addLayerRepaint(const QRect& r) { if (!compositing()) { return; } layer_repaints_region += r; emit needsRepaint(); } void Toplevel::addLayerRepaint(int x, int y, int w, int h) { QRect r(x, y, w, h); addLayerRepaint(r); } void Toplevel::addLayerRepaint(const QRegion& r) { if (!compositing()) return; layer_repaints_region += r; emit needsRepaint(); } void Toplevel::addRepaintFull() { repaints_region = visibleRect().translated(-pos()); emit needsRepaint(); } void Toplevel::resetRepaints() { repaints_region = QRegion(); layer_repaints_region = QRegion(); } void Toplevel::addWorkspaceRepaint(int x, int y, int w, int h) { addWorkspaceRepaint(QRect(x, y, w, h)); } void Toplevel::addWorkspaceRepaint(const QRect& r2) { if (!compositing()) return; Compositor::self()->addRepaint(r2); } bool Toplevel::updateUnredirectedState() { assert(compositing()); bool should = options->isUnredirectFullscreen() && shouldUnredirect() && !unredirectSuspend && !shape() && !hasAlpha() && opacity() == 1.0 && !static_cast(effects)->activeFullScreenEffect(); if (should == unredirect) return false; static QElapsedTimer lastUnredirect; static const qint64 msecRedirectInterval = 100; if (!lastUnredirect.hasExpired(msecRedirectInterval)) { QTimer::singleShot(msecRedirectInterval, Compositor::self(), SLOT(checkUnredirect())); return false; } lastUnredirect.start(); unredirect = should; if (unredirect) { qCDebug(KWIN_CORE) << "Unredirecting:" << this; xcb_composite_unredirect_window(connection(), frameId(), XCB_COMPOSITE_REDIRECT_MANUAL); } else { qCDebug(KWIN_CORE) << "Redirecting:" << this; xcb_composite_redirect_window(connection(), frameId(), XCB_COMPOSITE_REDIRECT_MANUAL); discardWindowPixmap(); } return true; } void Toplevel::suspendUnredirect(bool suspend) { if (unredirectSuspend == suspend) return; unredirectSuspend = suspend; Compositor::self()->checkUnredirect(); } //**************************************** // Client //**************************************** bool Client::setupCompositing() { if (!Toplevel::setupCompositing()){ return false; } if (isDecorated()) { decoratedClient()->destroyRenderer(); } updateVisibility(); // for internalKeep() return true; } void Client::finishCompositing(ReleaseReason releaseReason) { Toplevel::finishCompositing(releaseReason); updateVisibility(); if (!deleting) { if (isDecorated()) { decoratedClient()->destroyRenderer(); } } // for safety in case KWin is just resizing the window resetHaveResizeEffect(); } bool Client::shouldUnredirect() const { if (isActiveFullScreen()) { ToplevelList stacking = workspace()->xStackingOrder(); for (int pos = stacking.count() - 1; pos >= 0; --pos) { Toplevel* c = stacking.at(pos); if (c == this) // is not covered by any other window, ok to unredirect return true; if (c->geometry().intersects(geometry())) return false; } abort(); } return false; } //**************************************** // Unmanaged //**************************************** bool Unmanaged::shouldUnredirect() const { // the pixmap is needed for the login effect, a nicer solution would be the login effect increasing // refcount for the window pixmap (which would prevent unredirect), avoiding this hack if (resourceClass() == "ksplashx" || resourceClass() == "ksplashsimple" || resourceClass() == "ksplashqml" ) return false; // it must cover whole display or one xinerama screen, and be the topmost there const int desktop = VirtualDesktopManager::self()->current(); if (geometry() == workspace()->clientArea(FullArea, geometry().center(), desktop) || geometry() == workspace()->clientArea(ScreenArea, geometry().center(), desktop)) { ToplevelList stacking = workspace()->xStackingOrder(); for (int pos = stacking.count() - 1; pos >= 0; --pos) { Toplevel* c = stacking.at(pos); if (c == this) // is not covered by any other window, ok to unredirect return true; if (c->geometry().intersects(geometry())) return false; } abort(); } return false; } //**************************************** // Deleted //**************************************** bool Deleted::shouldUnredirect() const { return false; } } // namespace diff --git a/compositingprefs.cpp b/compositingprefs.cpp deleted file mode 100644 index ab7d16fc1..000000000 --- a/compositingprefs.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 2007 Rivo Laks - -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 "compositingprefs.h" - -#include "main.h" -#include "xcbutils.h" -#include "kwinglplatform.h" -#include "utils.h" - -#include -#include -#include - -#include -#include -#include -#include - - -namespace KWin -{ - -extern int screen_number; // main.cpp -extern bool is_multihead; - -CompositingPrefs::CompositingPrefs() -{ -} - -CompositingPrefs::~CompositingPrefs() -{ -} - -bool CompositingPrefs::openGlIsBroken() -{ - const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (is_multihead ? QString::number(screen_number) : QString())); - return KConfigGroup(kwinApp()->config(), "Compositing").readEntry(unsafeKey, false); -} - -bool CompositingPrefs::compositingPossible() -{ - // first off, check whether we figured that we'll crash on detection because of a buggy driver - KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); - const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (is_multihead ? QString::number(screen_number) : QString())); - if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && - gl_workaround_group.readEntry(unsafeKey, false)) - return false; - - if (kwinApp()->shouldUseWaylandForCompositing()) { - // don't do X specific checks if we are not going to use X for compositing - return true; - } - - if (!Xcb::Extensions::self()->isCompositeAvailable()) { - qCDebug(KWIN_CORE) << "No composite extension available"; - return false; - } - if (!Xcb::Extensions::self()->isDamageAvailable()) { - qCDebug(KWIN_CORE) << "No damage extension available"; - return false; - } - if (hasGlx()) - return true; -#ifdef KWIN_HAVE_XRENDER_COMPOSITING - if (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable()) - return true; -#endif - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - return true; - } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { - return true; - } - qCDebug(KWIN_CORE) << "No OpenGL or XRender/XFixes support"; - return false; -} - -QString CompositingPrefs::compositingNotPossibleReason() -{ - // first off, check whether we figured that we'll crash on detection because of a buggy driver - KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); - const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (is_multihead ? QString::number(screen_number) : QString())); - if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && - gl_workaround_group.readEntry(unsafeKey, false)) - return i18n("OpenGL compositing (the default) has crashed KWin in the past.
" - "This was most likely due to a driver bug." - "

If you think that you have meanwhile upgraded to a stable driver,
" - "you can reset this protection but be aware that this might result in an immediate crash!

" - "

Alternatively, you might want to use the XRender backend instead.

"); - - if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) { - return i18n("Required X extensions (XComposite and XDamage) are not available."); - } -#if !defined( KWIN_HAVE_XRENDER_COMPOSITING ) - if (!hasGlx()) - return i18n("GLX/OpenGL are not available and only OpenGL support is compiled."); -#else - if (!(hasGlx() - || (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable()))) { - return i18n("GLX/OpenGL and XRender/XFixes are not available."); - } -#endif - return QString(); -} - -bool CompositingPrefs::hasGlx() -{ - return Xcb::Extensions::self()->hasGlx(); -} - -} // namespace - diff --git a/compositingprefs.h b/compositingprefs.h deleted file mode 100644 index 889ed8b97..000000000 --- a/compositingprefs.h +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 2007 Rivo Laks - -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_COMPOSITINGPREFS_H -#define KWIN_COMPOSITINGPREFS_H - -#include -#include - -#include "kwinglutils.h" -#include "kwinglobals.h" - - -namespace KWin -{ - -class CompositingPrefs -{ -public: - CompositingPrefs(); - ~CompositingPrefs(); - - static bool compositingPossible(); - static QString compositingNotPossibleReason(); - static bool openGlIsBroken(); - /** - * Tests whether GLX is supported and returns @c true - * in case KWin is compiled with OpenGL support and GLX - * is available. - * - * If KWin is compiled with OpenGL ES or without OpenGL at - * all, @c false is returned. - * @returns @c true if GLX is available, @c false otherwise and if not build with OpenGL support. - **/ - static bool hasGlx(); -}; - -} - -#endif //KWIN_COMPOSITINGPREFS_H - - diff --git a/dbusinterface.cpp b/dbusinterface.cpp index 99dd7762b..302355bd9 100644 --- a/dbusinterface.cpp +++ b/dbusinterface.cpp @@ -1,265 +1,265 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012 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 "dbusinterface.h" #include "compositingadaptor.h" // kwin #include "atoms.h" #include "composite.h" -#include "compositingprefs.h" #include "debug_console.h" #include "main.h" #include "placement.h" +#include "platform.h" #include "kwinadaptor.h" #include "scene.h" #include "workspace.h" #include "virtualdesktops.h" #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif // Qt #include #include namespace KWin { DBusInterface::DBusInterface(QObject *parent) : QObject(parent) , m_serviceName(QStringLiteral("org.kde.KWin")) { (void) new KWinAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/KWin"), this); const QByteArray dBusSuffix = qgetenv("KWIN_DBUS_SERVICE_SUFFIX"); if (!dBusSuffix.isNull()) { m_serviceName = m_serviceName + QLatin1Char('.') + dBusSuffix; } if (!dbus.registerService(m_serviceName)) { QDBusServiceWatcher *dog = new QDBusServiceWatcher(m_serviceName, dbus, QDBusServiceWatcher::WatchForUnregistration, this); connect (dog, SIGNAL(serviceUnregistered(QString)), SLOT(becomeKWinService(QString))); } else { announceService(); } dbus.connect(QString(), QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig"), Workspace::self(), SLOT(slotReloadConfig())); } void DBusInterface::becomeKWinService(const QString &service) { // TODO: this watchdog exists to make really safe that we at some point get the service // but it's probably no longer needed since we explicitly unregister the service with the deconstructor if (service == m_serviceName && QDBusConnection::sessionBus().registerService(m_serviceName) && sender()) { sender()->deleteLater(); // bye doggy :'( announceService(); } } DBusInterface::~DBusInterface() { QDBusConnection::sessionBus().unregisterService(m_serviceName); // KApplication automatically also grabs org.kde.kwin, so it's often been used externally - ensure to free it as well QDBusConnection::sessionBus().unregisterService(QStringLiteral("org.kde.kwin")); xcb_delete_property(connection(), rootWindow(), atoms->kwin_dbus_service); } void DBusInterface::announceService() { const QByteArray service = m_serviceName.toUtf8(); xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, rootWindow(), atoms->kwin_dbus_service, atoms->utf8_string, 8, service.size(), service.constData()); } // wrap void methods with no arguments to Workspace #define WRAP(name) \ void DBusInterface::name() \ {\ Workspace::self()->name();\ } WRAP(reconfigure) #undef WRAP void DBusInterface::killWindow() { Workspace::self()->slotKillWindow(); } #define WRAP(name) \ void DBusInterface::name() \ {\ Placement::self()->name();\ } WRAP(cascadeDesktop) WRAP(unclutterDesktop) #undef WRAP // wrap returning methods with no arguments to Workspace #define WRAP( rettype, name ) \ rettype DBusInterface::name( ) \ {\ return Workspace::self()->name(); \ } WRAP(QString, supportInformation) #undef WRAP bool DBusInterface::startActivity(const QString &in0) { #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return false; } return Activities::self()->start(in0); #else Q_UNUSED(in0) return false; #endif } bool DBusInterface::stopActivity(const QString &in0) { #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return false; } return Activities::self()->stop(in0); #else Q_UNUSED(in0) return false; #endif } int DBusInterface::currentDesktop() { return VirtualDesktopManager::self()->current(); } bool DBusInterface::setCurrentDesktop(int desktop) { return VirtualDesktopManager::self()->setCurrent(desktop); } void DBusInterface::nextDesktop() { VirtualDesktopManager::self()->moveTo(); } void DBusInterface::previousDesktop() { VirtualDesktopManager::self()->moveTo(); } void DBusInterface::showDebugConsole() { DebugConsole *console = new DebugConsole; console->show(); } CompositorDBusInterface::CompositorDBusInterface(Compositor *parent) : QObject(parent) , m_compositor(parent) { connect(m_compositor, &Compositor::compositingToggled, this, &CompositorDBusInterface::compositingToggled); new CompositingAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Compositor"), this); dbus.connect(QString(), QStringLiteral("/Compositor"), QStringLiteral("org.kde.kwin.Compositing"), QStringLiteral("reinit"), m_compositor, SLOT(slotReinitialize())); } QString CompositorDBusInterface::compositingNotPossibleReason() const { - return CompositingPrefs::compositingNotPossibleReason(); + return kwinApp()->platform()->compositingNotPossibleReason(); } QString CompositorDBusInterface::compositingType() const { if (!m_compositor->hasScene()) { return QStringLiteral("none"); } switch (m_compositor->scene()->compositingType()) { case XRenderCompositing: return QStringLiteral("xrender"); case OpenGL2Compositing: if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { return QStringLiteral("gles"); } else { return QStringLiteral("gl2"); } case QPainterCompositing: return QStringLiteral("qpainter"); case NoCompositing: default: return QStringLiteral("none"); } } bool CompositorDBusInterface::isActive() const { return m_compositor->isActive(); } bool CompositorDBusInterface::isCompositingPossible() const { - return CompositingPrefs::compositingPossible(); + return kwinApp()->platform()->compositingPossible(); } bool CompositorDBusInterface::isOpenGLBroken() const { - return CompositingPrefs::openGlIsBroken(); + return kwinApp()->platform()->openGLCompositingIsBroken(); } void CompositorDBusInterface::resume() { m_compositor->resume(Compositor::ScriptSuspend); } void CompositorDBusInterface::suspend() { m_compositor->suspend(Compositor::ScriptSuspend); } QStringList CompositorDBusInterface::supportedOpenGLPlatformInterfaces() const { QStringList interfaces; bool supportsGlx = false; #if HAVE_EPOXY_GLX supportsGlx = (kwinApp()->operationMode() == Application::OperationModeX11); #endif if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { supportsGlx = false; } if (supportsGlx) { interfaces << QStringLiteral("glx"); } interfaces << QStringLiteral("egl"); return interfaces; } } // namespace diff --git a/options.cpp b/options.cpp index 9054cffde..dbd957fed 100644 --- a/options.cpp +++ b/options.cpp @@ -1,1172 +1,1171 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2012 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 "options.h" #include "config-kwin.h" #include "utils.h" #include "platform.h" #ifndef KCMRULES #include -#include "compositingprefs.h" #include "screens.h" #include "settings.h" #include "xcbutils.h" #include #include #endif //KCMRULES namespace KWin { #ifndef KCMRULES int currentRefreshRate() { int rate = -1; QString syncScreenName(QLatin1String("primary screen")); if (options->refreshRate() > 0) { // use manually configured refresh rate rate = options->refreshRate(); } else if (Screens::self()->count() > 0) { // prefer the refreshrate calculated from the screens mode information // at least the nvidia driver reports 50Hz BS ... *again*! int syncScreen = 0; if (Screens::self()->count() > 1) { const QByteArray syncDisplayDevice(qgetenv("__GL_SYNC_DISPLAY_DEVICE")); // if __GL_SYNC_DISPLAY_DEVICE is exported, the GPU shall sync to that device // so we try to use its refresh rate if (!syncDisplayDevice.isEmpty()) { for (int i = 0; i < Screens::self()->count(); ++i) { if (Screens::self()->name(i) == syncDisplayDevice) { syncScreenName = Screens::self()->name(i); syncScreen = i; break; } } } } rate = qRound(Screens::self()->refreshRate(syncScreen)); // TODO forward float precision? } else if (Xcb::Extensions::self()->isRandrAvailable()) { // last restort - query XRandR screenInfo rate - probably wrong on nvidia systems Xcb::RandR::ScreenInfo screenInfo(rootWindow()); rate = screenInfo->rate; } // 0Hz or less is invalid, so we fallback to a default rate if (rate <= 0) rate = 60; // and not shitty 50Hz for sure! *grrr* // QTimer gives us 1msec (1000Hz) at best, so we ignore anything higher; // however, additional throttling prevents very high rates from taking place anyway else if (rate > 1000) rate = 1000; qCDebug(KWIN_CORE) << "Vertical Refresh rate " << rate << "Hz (" << syncScreenName << ")"; return rate; } Options::Options(QObject *parent) : QObject(parent) , m_settings(new Settings(kwinApp()->config())) , m_focusPolicy(ClickToFocus) , m_nextFocusPrefersMouse(false) , m_clickRaise(false) , m_autoRaise(false) , m_autoRaiseInterval(0) , m_delayFocusInterval(0) , m_shadeHover(false) , m_shadeHoverInterval(0) , m_separateScreenFocus(false) , m_placement(Placement::NoPlacement) , m_borderSnapZone(0) , m_windowSnapZone(0) , m_centerSnapZone(0) , m_snapOnlyWhenOverlapping(false) , m_rollOverDesktops(false) , m_focusStealingPreventionLevel(0) , m_legacyFullscreenSupport(false) , m_killPingTimeout(0) , m_hideUtilityWindowsForInactive(false) , m_inactiveTabsSkipTaskbar(false) , m_autogroupSimilarWindows(false) , m_autogroupInForeground(false) , m_compositingMode(Options::defaultCompositingMode()) , m_useCompositing(Options::defaultUseCompositing()) , m_compositingInitialized(Options::defaultCompositingInitialized()) , m_hiddenPreviews(Options::defaultHiddenPreviews()) , m_unredirectFullscreen(Options::defaultUnredirectFullscreen()) , m_glSmoothScale(Options::defaultGlSmoothScale()) , m_colorCorrected(Options::defaultColorCorrected()) , m_xrenderSmoothScale(Options::defaultXrenderSmoothScale()) , m_maxFpsInterval(Options::defaultMaxFpsInterval()) , m_refreshRate(Options::defaultRefreshRate()) , m_vBlankTime(Options::defaultVBlankTime()) , m_glStrictBinding(Options::defaultGlStrictBinding()) , m_glStrictBindingFollowsDriver(Options::defaultGlStrictBindingFollowsDriver()) , m_glCoreProfile(Options::defaultGLCoreProfile()) , m_glPreferBufferSwap(Options::defaultGlPreferBufferSwap()) , m_glPlatformInterface(Options::defaultGlPlatformInterface()) , OpTitlebarDblClick(Options::defaultOperationTitlebarDblClick()) , CmdActiveTitlebar1(Options::defaultCommandActiveTitlebar1()) , CmdActiveTitlebar2(Options::defaultCommandActiveTitlebar2()) , CmdActiveTitlebar3(Options::defaultCommandActiveTitlebar3()) , CmdInactiveTitlebar1(Options::defaultCommandInactiveTitlebar1()) , CmdInactiveTitlebar2(Options::defaultCommandInactiveTitlebar2()) , CmdInactiveTitlebar3(Options::defaultCommandInactiveTitlebar3()) , CmdTitlebarWheel(Options::defaultCommandTitlebarWheel()) , CmdWindow1(Options::defaultCommandWindow1()) , CmdWindow2(Options::defaultCommandWindow2()) , CmdWindow3(Options::defaultCommandWindow3()) , CmdWindowWheel(Options::defaultCommandWindowWheel()) , CmdAll1(Options::defaultCommandAll1()) , CmdAll2(Options::defaultCommandAll2()) , CmdAll3(Options::defaultCommandAll3()) , CmdAllWheel(Options::defaultCommandAllWheel()) , CmdAllModKey(Options::defaultKeyCmdAllModKey()) , electric_border_maximize(false) , electric_border_tiling(false) , electric_border_corner_ratio(0.0) , borderless_maximized_windows(false) , show_geometry_tip(false) , condensed_title(false) , animationSpeed(Options::defaultAnimationSpeed()) { m_settings->setDefaults(); syncFromKcfgc(); } Options::~Options() { } void Options::setFocusPolicy(FocusPolicy focusPolicy) { if (m_focusPolicy == focusPolicy) { return; } m_focusPolicy = focusPolicy; emit focusPolicyChanged(); if (m_focusPolicy == ClickToFocus) { setAutoRaise(false); setAutoRaiseInterval(0); setDelayFocusInterval(0); } } void Options::setNextFocusPrefersMouse(bool nextFocusPrefersMouse) { if (m_nextFocusPrefersMouse == nextFocusPrefersMouse) { return; } m_nextFocusPrefersMouse = nextFocusPrefersMouse; emit nextFocusPrefersMouseChanged(); } void Options::setClickRaise(bool clickRaise) { if (m_autoRaise) { // important: autoRaise implies ClickRaise clickRaise = true; } if (m_clickRaise == clickRaise) { return; } m_clickRaise = clickRaise; emit clickRaiseChanged(); } void Options::setAutoRaise(bool autoRaise) { if (m_focusPolicy == ClickToFocus) { autoRaise = false; } if (m_autoRaise == autoRaise) { return; } m_autoRaise = autoRaise; if (m_autoRaise) { // important: autoRaise implies ClickRaise setClickRaise(true); } emit autoRaiseChanged(); } void Options::setAutoRaiseInterval(int autoRaiseInterval) { if (m_focusPolicy == ClickToFocus) { autoRaiseInterval = 0; } if (m_autoRaiseInterval == autoRaiseInterval) { return; } m_autoRaiseInterval = autoRaiseInterval; emit autoRaiseIntervalChanged(); } void Options::setDelayFocusInterval(int delayFocusInterval) { if (m_focusPolicy == ClickToFocus) { delayFocusInterval = 0; } if (m_delayFocusInterval == delayFocusInterval) { return; } m_delayFocusInterval = delayFocusInterval; emit delayFocusIntervalChanged(); } void Options::setShadeHover(bool shadeHover) { if (m_shadeHover == shadeHover) { return; } m_shadeHover = shadeHover; emit shadeHoverChanged(); } void Options::setShadeHoverInterval(int shadeHoverInterval) { if (m_shadeHoverInterval == shadeHoverInterval) { return; } m_shadeHoverInterval = shadeHoverInterval; emit shadeHoverIntervalChanged(); } void Options::setSeparateScreenFocus(bool separateScreenFocus) { if (m_separateScreenFocus == separateScreenFocus) { return; } m_separateScreenFocus = separateScreenFocus; emit separateScreenFocusChanged(m_separateScreenFocus); } void Options::setPlacement(int placement) { if (m_placement == static_cast(placement)) { return; } m_placement = static_cast(placement); emit placementChanged(); } void Options::setBorderSnapZone(int borderSnapZone) { if (m_borderSnapZone == borderSnapZone) { return; } m_borderSnapZone = borderSnapZone; emit borderSnapZoneChanged(); } void Options::setWindowSnapZone(int windowSnapZone) { if (m_windowSnapZone == windowSnapZone) { return; } m_windowSnapZone = windowSnapZone; emit windowSnapZoneChanged(); } void Options::setCenterSnapZone(int centerSnapZone) { if (m_centerSnapZone == centerSnapZone) { return; } m_centerSnapZone = centerSnapZone; emit centerSnapZoneChanged(); } void Options::setSnapOnlyWhenOverlapping(bool snapOnlyWhenOverlapping) { if (m_snapOnlyWhenOverlapping == snapOnlyWhenOverlapping) { return; } m_snapOnlyWhenOverlapping = snapOnlyWhenOverlapping; emit snapOnlyWhenOverlappingChanged(); } void Options::setRollOverDesktops(bool rollOverDesktops) { if (m_rollOverDesktops == rollOverDesktops) { return; } m_rollOverDesktops = rollOverDesktops; emit rollOverDesktopsChanged(m_rollOverDesktops); } void Options::setFocusStealingPreventionLevel(int focusStealingPreventionLevel) { if (!focusPolicyIsReasonable()) { focusStealingPreventionLevel = 0; } if (m_focusStealingPreventionLevel == focusStealingPreventionLevel) { return; } m_focusStealingPreventionLevel = qMax(0, qMin(4, focusStealingPreventionLevel)); emit focusStealingPreventionLevelChanged(); } void Options::setLegacyFullscreenSupport(bool legacyFullscreenSupport) { if (m_legacyFullscreenSupport == legacyFullscreenSupport) { return; } m_legacyFullscreenSupport = legacyFullscreenSupport; emit legacyFullscreenSupportChanged(); } void Options::setOperationTitlebarDblClick(WindowOperation operationTitlebarDblClick) { if (OpTitlebarDblClick == operationTitlebarDblClick) { return; } OpTitlebarDblClick = operationTitlebarDblClick; emit operationTitlebarDblClickChanged(); } void Options::setOperationMaxButtonLeftClick(WindowOperation op) { if (opMaxButtonLeftClick == op) { return; } opMaxButtonLeftClick = op; emit operationMaxButtonLeftClickChanged(); } void Options::setOperationMaxButtonRightClick(WindowOperation op) { if (opMaxButtonRightClick == op) { return; } opMaxButtonRightClick = op; emit operationMaxButtonRightClickChanged(); } void Options::setOperationMaxButtonMiddleClick(WindowOperation op) { if (opMaxButtonMiddleClick == op) { return; } opMaxButtonMiddleClick = op; emit operationMaxButtonMiddleClickChanged(); } void Options::setCommandActiveTitlebar1(MouseCommand commandActiveTitlebar1) { if (CmdActiveTitlebar1 == commandActiveTitlebar1) { return; } CmdActiveTitlebar1 = commandActiveTitlebar1; emit commandActiveTitlebar1Changed(); } void Options::setCommandActiveTitlebar2(MouseCommand commandActiveTitlebar2) { if (CmdActiveTitlebar2 == commandActiveTitlebar2) { return; } CmdActiveTitlebar2 = commandActiveTitlebar2; emit commandActiveTitlebar2Changed(); } void Options::setCommandActiveTitlebar3(MouseCommand commandActiveTitlebar3) { if (CmdActiveTitlebar3 == commandActiveTitlebar3) { return; } CmdActiveTitlebar3 = commandActiveTitlebar3; emit commandActiveTitlebar3Changed(); } void Options::setCommandInactiveTitlebar1(MouseCommand commandInactiveTitlebar1) { if (CmdInactiveTitlebar1 == commandInactiveTitlebar1) { return; } CmdInactiveTitlebar1 = commandInactiveTitlebar1; emit commandInactiveTitlebar1Changed(); } void Options::setCommandInactiveTitlebar2(MouseCommand commandInactiveTitlebar2) { if (CmdInactiveTitlebar2 == commandInactiveTitlebar2) { return; } CmdInactiveTitlebar2 = commandInactiveTitlebar2; emit commandInactiveTitlebar2Changed(); } void Options::setCommandInactiveTitlebar3(MouseCommand commandInactiveTitlebar3) { if (CmdInactiveTitlebar3 == commandInactiveTitlebar3) { return; } CmdInactiveTitlebar3 = commandInactiveTitlebar3; emit commandInactiveTitlebar3Changed(); } void Options::setCommandWindow1(MouseCommand commandWindow1) { if (CmdWindow1 == commandWindow1) { return; } CmdWindow1 = commandWindow1; emit commandWindow1Changed(); } void Options::setCommandWindow2(MouseCommand commandWindow2) { if (CmdWindow2 == commandWindow2) { return; } CmdWindow2 = commandWindow2; emit commandWindow2Changed(); } void Options::setCommandWindow3(MouseCommand commandWindow3) { if (CmdWindow3 == commandWindow3) { return; } CmdWindow3 = commandWindow3; emit commandWindow3Changed(); } void Options::setCommandWindowWheel(MouseCommand commandWindowWheel) { if (CmdWindowWheel == commandWindowWheel) { return; } CmdWindowWheel = commandWindowWheel; emit commandWindowWheelChanged(); } void Options::setCommandAll1(MouseCommand commandAll1) { if (CmdAll1 == commandAll1) { return; } CmdAll1 = commandAll1; emit commandAll1Changed(); } void Options::setCommandAll2(MouseCommand commandAll2) { if (CmdAll2 == commandAll2) { return; } CmdAll2 = commandAll2; emit commandAll2Changed(); } void Options::setCommandAll3(MouseCommand commandAll3) { if (CmdAll3 == commandAll3) { return; } CmdAll3 = commandAll3; emit commandAll3Changed(); } void Options::setKeyCmdAllModKey(uint keyCmdAllModKey) { if (CmdAllModKey == keyCmdAllModKey) { return; } CmdAllModKey = keyCmdAllModKey; emit keyCmdAllModKeyChanged(); } void Options::setShowGeometryTip(bool showGeometryTip) { if (show_geometry_tip == showGeometryTip) { return; } show_geometry_tip = showGeometryTip; emit showGeometryTipChanged(); } void Options::setCondensedTitle(bool condensedTitle) { if (condensed_title == condensedTitle) { return; } condensed_title = condensedTitle; emit condensedTitleChanged(); } void Options::setElectricBorderMaximize(bool electricBorderMaximize) { if (electric_border_maximize == electricBorderMaximize) { return; } electric_border_maximize = electricBorderMaximize; emit electricBorderMaximizeChanged(); } void Options::setElectricBorderTiling(bool electricBorderTiling) { if (electric_border_tiling == electricBorderTiling) { return; } electric_border_tiling = electricBorderTiling; emit electricBorderTilingChanged(); } void Options::setElectricBorderCornerRatio(float electricBorderCornerRatio) { if (electric_border_corner_ratio == electricBorderCornerRatio) { return; } electric_border_corner_ratio = electricBorderCornerRatio; emit electricBorderCornerRatioChanged(); } void Options::setBorderlessMaximizedWindows(bool borderlessMaximizedWindows) { if (borderless_maximized_windows == borderlessMaximizedWindows) { return; } borderless_maximized_windows = borderlessMaximizedWindows; emit borderlessMaximizedWindowsChanged(); } void Options::setKillPingTimeout(int killPingTimeout) { if (m_killPingTimeout == killPingTimeout) { return; } m_killPingTimeout = killPingTimeout; emit killPingTimeoutChanged(); } void Options::setHideUtilityWindowsForInactive(bool hideUtilityWindowsForInactive) { if (m_hideUtilityWindowsForInactive == hideUtilityWindowsForInactive) { return; } m_hideUtilityWindowsForInactive = hideUtilityWindowsForInactive; emit hideUtilityWindowsForInactiveChanged(); } void Options::setInactiveTabsSkipTaskbar(bool inactiveTabsSkipTaskbar) { if (m_inactiveTabsSkipTaskbar == inactiveTabsSkipTaskbar) { return; } m_inactiveTabsSkipTaskbar = inactiveTabsSkipTaskbar; emit inactiveTabsSkipTaskbarChanged(); } void Options::setAutogroupSimilarWindows(bool autogroupSimilarWindows) { if (m_autogroupSimilarWindows == autogroupSimilarWindows) { return; } m_autogroupSimilarWindows = autogroupSimilarWindows; emit autogroupSimilarWindowsChanged(); } void Options::setAutogroupInForeground(bool autogroupInForeground) { if (m_autogroupInForeground == autogroupInForeground) { return; } m_autogroupInForeground = autogroupInForeground; emit autogroupInForegroundChanged(); } void Options::setCompositingMode(int compositingMode) { if (m_compositingMode == static_cast(compositingMode)) { return; } m_compositingMode = static_cast(compositingMode); emit compositingModeChanged(); } void Options::setUseCompositing(bool useCompositing) { if (m_useCompositing == useCompositing) { return; } m_useCompositing = useCompositing; emit useCompositingChanged(); } void Options::setCompositingInitialized(bool compositingInitialized) { if (m_compositingInitialized == compositingInitialized) { return; } m_compositingInitialized = compositingInitialized; emit compositingInitializedChanged(); } void Options::setHiddenPreviews(int hiddenPreviews) { if (m_hiddenPreviews == static_cast(hiddenPreviews)) { return; } m_hiddenPreviews = static_cast(hiddenPreviews); emit hiddenPreviewsChanged(); } void Options::setUnredirectFullscreen(bool unredirectFullscreen) { if (GLPlatform::instance()->driver() == Driver_Intel) unredirectFullscreen = false; // bug #252817 if (m_unredirectFullscreen == unredirectFullscreen) { return; } if (GLPlatform::instance()->driver() == Driver_Intel) { // write back the value KConfigGroup(m_settings->config(), "Compositing").writeEntry("UnredirectFullscreen", false); } m_unredirectFullscreen = unredirectFullscreen; emit unredirectFullscreenChanged(); } void Options::setGlSmoothScale(int glSmoothScale) { if (m_glSmoothScale == glSmoothScale) { return; } m_glSmoothScale = glSmoothScale; emit glSmoothScaleChanged(); } void Options::setColorCorrected(bool colorCorrected) { if (m_colorCorrected == colorCorrected) { return; } m_colorCorrected = colorCorrected; emit colorCorrectedChanged(); } void Options::setXrenderSmoothScale(bool xrenderSmoothScale) { if (m_xrenderSmoothScale == xrenderSmoothScale) { return; } m_xrenderSmoothScale = xrenderSmoothScale; emit xrenderSmoothScaleChanged(); } void Options::setMaxFpsInterval(qint64 maxFpsInterval) { if (m_maxFpsInterval == maxFpsInterval) { return; } m_maxFpsInterval = maxFpsInterval; emit maxFpsIntervalChanged(); } void Options::setRefreshRate(uint refreshRate) { if (m_refreshRate == refreshRate) { return; } m_refreshRate = refreshRate; emit refreshRateChanged(); } void Options::setVBlankTime(qint64 vBlankTime) { if (m_vBlankTime == vBlankTime) { return; } m_vBlankTime = vBlankTime; emit vBlankTimeChanged(); } void Options::setGlStrictBinding(bool glStrictBinding) { if (m_glStrictBinding == glStrictBinding) { return; } m_glStrictBinding = glStrictBinding; emit glStrictBindingChanged(); } void Options::setGlStrictBindingFollowsDriver(bool glStrictBindingFollowsDriver) { if (m_glStrictBindingFollowsDriver == glStrictBindingFollowsDriver) { return; } m_glStrictBindingFollowsDriver = glStrictBindingFollowsDriver; emit glStrictBindingFollowsDriverChanged(); } void Options::setGLCoreProfile(bool value) { if (m_glCoreProfile == value) { return; } m_glCoreProfile = value; emit glCoreProfileChanged(); } void Options::setGlPreferBufferSwap(char glPreferBufferSwap) { if (glPreferBufferSwap == 'a') { // buffer cpying is very fast with the nvidia blob // but due to restrictions in DRI2 *incredibly* slow for all MESA drivers // see http://www.x.org/releases/X11R7.7/doc/dri2proto/dri2proto.txt, item 2.5 if (GLPlatform::instance()->driver() == Driver_NVidia) glPreferBufferSwap = CopyFrontBuffer; else if (GLPlatform::instance()->driver() != Driver_Unknown) // undetected, finally resolved when context is initialized glPreferBufferSwap = ExtendDamage; } if (m_glPreferBufferSwap == (GlSwapStrategy)glPreferBufferSwap) { return; } m_glPreferBufferSwap = (GlSwapStrategy)glPreferBufferSwap; emit glPreferBufferSwapChanged(); } void Options::setGlPlatformInterface(OpenGLPlatformInterface interface) { // check environment variable const QByteArray envOpenGLInterface(qgetenv("KWIN_OPENGL_INTERFACE")); if (!envOpenGLInterface.isEmpty()) { if (qstrcmp(envOpenGLInterface, "egl") == 0) { qCDebug(KWIN_CORE) << "Forcing EGL native interface through environment variable"; interface = EglPlatformInterface; } else if (qstrcmp(envOpenGLInterface, "glx") == 0) { qCDebug(KWIN_CORE) << "Forcing GLX native interface through environment variable"; interface = GlxPlatformInterface; } } if (kwinApp()->shouldUseWaylandForCompositing() && interface == GlxPlatformInterface) { // Glx is impossible on Wayland, enforce egl qCDebug(KWIN_CORE) << "Forcing EGL native interface for Wayland mode"; interface = EglPlatformInterface; } #if !HAVE_EPOXY_GLX qCDebug(KWIN_CORE) << "Forcing EGL native interface as compiled without GLX support"; interface = EglPlatformInterface; #endif if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { qCDebug(KWIN_CORE) << "Forcing EGL native interface as Qt uses OpenGL ES"; interface = EglPlatformInterface; } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { qCDebug(KWIN_CORE) << "Forcing EGL native interface as OpenGL ES requested through KWIN_COMPOSE environment variable."; interface = EglPlatformInterface; } if (m_glPlatformInterface == interface) { return; } m_glPlatformInterface = interface; emit glPlatformInterfaceChanged(); } void Options::reparseConfiguration() { m_settings->config()->reparseConfiguration(); } void Options::updateSettings() { loadConfig(); // Read button tooltip animation effect from kdeglobals // Since we want to allow users to enable window decoration tooltips // and not kstyle tooltips and vise-versa, we don't read the // "EffectNoTooltip" setting from kdeglobals. // QToolTip::setGloballyEnabled( d->show_tooltips ); // KDE4 this probably needs to be done manually in clients // Driver-specific config detection setCompositingInitialized(false); reloadCompositingSettings(); emit configChanged(); } void Options::loadConfig() { m_settings->load(); syncFromKcfgc(); // Electric borders KConfigGroup config(m_settings->config(), "Windows"); OpTitlebarDblClick = windowOperation(config.readEntry("TitlebarDoubleClickCommand", "Maximize"), true); setOperationMaxButtonLeftClick(windowOperation(config.readEntry("MaximizeButtonLeftClickCommand", "Maximize"), true)); setOperationMaxButtonMiddleClick(windowOperation(config.readEntry("MaximizeButtonMiddleClickCommand", "Maximize (vertical only)"), true)); setOperationMaxButtonRightClick(windowOperation(config.readEntry("MaximizeButtonRightClickCommand", "Maximize (horizontal only)"), true)); // Mouse bindings config = KConfigGroup(m_settings->config(), "MouseBindings"); // TODO: add properties for missing options CmdTitlebarWheel = mouseWheelCommand(config.readEntry("CommandTitlebarWheel", "Switch to Window Tab to the Left/Right")); CmdAllModKey = (config.readEntry("CommandAllKey", "Alt") == QStringLiteral("Meta")) ? Qt::Key_Meta : Qt::Key_Alt; CmdAllWheel = mouseWheelCommand(config.readEntry("CommandAllWheel", "Nothing")); setCommandActiveTitlebar1(mouseCommand(config.readEntry("CommandActiveTitlebar1", "Raise"), true)); setCommandActiveTitlebar2(mouseCommand(config.readEntry("CommandActiveTitlebar2", "Start Window Tab Drag"), true)); setCommandActiveTitlebar3(mouseCommand(config.readEntry("CommandActiveTitlebar3", "Operations menu"), true)); setCommandInactiveTitlebar1(mouseCommand(config.readEntry("CommandInactiveTitlebar1", "Activate and raise"), true)); setCommandInactiveTitlebar2(mouseCommand(config.readEntry("CommandInactiveTitlebar2", "Start Window Tab Drag"), true)); setCommandInactiveTitlebar3(mouseCommand(config.readEntry("CommandInactiveTitlebar3", "Operations menu"), true)); setCommandWindow1(mouseCommand(config.readEntry("CommandWindow1", "Activate, raise and pass click"), false)); setCommandWindow2(mouseCommand(config.readEntry("CommandWindow2", "Activate and pass click"), false)); setCommandWindow3(mouseCommand(config.readEntry("CommandWindow3", "Activate and pass click"), false)); setCommandWindowWheel(mouseCommand(config.readEntry("CommandWindowWheel", "Scroll"), false)); setCommandAll1(mouseCommand(config.readEntry("CommandAll1", "Move"), false)); setCommandAll2(mouseCommand(config.readEntry("CommandAll2", "Toggle raise and lower"), false)); setCommandAll3(mouseCommand(config.readEntry("CommandAll3", "Resize"), false)); // TODO: should they be moved into reloadCompositingSettings? config = KConfigGroup(m_settings->config(), "Compositing"); setMaxFpsInterval(1 * 1000 * 1000 * 1000 / config.readEntry("MaxFPS", Options::defaultMaxFps())); setRefreshRate(config.readEntry("RefreshRate", Options::defaultRefreshRate())); setVBlankTime(config.readEntry("VBlankTime", Options::defaultVBlankTime()) * 1000); // config in micro, value in nano resolution // Modifier Only Shortcuts config = KConfigGroup(m_settings->config(), "ModifierOnlyShortcuts"); m_modifierOnlyShortcuts.clear(); if (config.hasKey("Shift")) { m_modifierOnlyShortcuts.insert(Qt::ShiftModifier, config.readEntry("Shift", QStringList())); } if (config.hasKey("Control")) { m_modifierOnlyShortcuts.insert(Qt::ControlModifier, config.readEntry("Control", QStringList())); } if (config.hasKey("Alt")) { m_modifierOnlyShortcuts.insert(Qt::AltModifier, config.readEntry("Alt", QStringList())); } if (config.hasKey("Meta")) { m_modifierOnlyShortcuts.insert(Qt::MetaModifier, config.readEntry("Meta", QStringList())); } } void Options::syncFromKcfgc() { setShowGeometryTip(m_settings->geometryTip()); setCondensedTitle(m_settings->condensedTitle()); setFocusPolicy(m_settings->focusPolicy()); setNextFocusPrefersMouse(m_settings->nextFocusPrefersMouse()); setSeparateScreenFocus(m_settings->separateScreenFocus()); setRollOverDesktops(m_settings->rollOverDesktops()); setLegacyFullscreenSupport(m_settings->legacyFullscreenSupport()); setFocusStealingPreventionLevel(m_settings->focusStealingPreventionLevel()); #ifdef KWIN_BUILD_DECORATIONS setPlacement(m_settings->placement()); #else setPlacement(Placement::Maximizing); #endif setAutoRaise(m_settings->autoRaise()); setAutoRaiseInterval(m_settings->autoRaiseInterval()); setDelayFocusInterval(m_settings->delayFocusInterval()); setShadeHover(m_settings->shadeHover()); setShadeHoverInterval(m_settings->shadeHoverInterval()); setClickRaise(m_settings->clickRaise()); setBorderSnapZone(m_settings->borderSnapZone()); setWindowSnapZone(m_settings->windowSnapZone()); setCenterSnapZone(m_settings->centerSnapZone()); setSnapOnlyWhenOverlapping(m_settings->snapOnlyWhenOverlapping()); setKillPingTimeout(m_settings->killPingTimeout()); setHideUtilityWindowsForInactive(m_settings->hideUtilityWindowsForInactive()); setInactiveTabsSkipTaskbar(m_settings->inactiveTabsSkipTaskbar()); setAutogroupSimilarWindows(m_settings->autogroupSimilarWindows()); setAutogroupInForeground(m_settings->autogroupInForeground()); setBorderlessMaximizedWindows(m_settings->borderlessMaximizedWindows()); setElectricBorderMaximize(m_settings->electricBorderMaximize()); setElectricBorderTiling(m_settings->electricBorderTiling()); setElectricBorderCornerRatio(m_settings->electricBorderCornerRatio()); } bool Options::loadCompositingConfig (bool force) { KConfigGroup config(m_settings->config(), "Compositing"); bool useCompositing = false; CompositingType compositingMode = NoCompositing; QString compositingBackend = config.readEntry("Backend", "OpenGL"); if (compositingBackend == QStringLiteral("XRender")) compositingMode = XRenderCompositing; else if (compositingBackend == "QPainter") compositingMode = QPainterCompositing; else compositingMode = OpenGLCompositing; if (const char *c = getenv("KWIN_COMPOSE")) { switch(c[0]) { case 'O': qCDebug(KWIN_CORE) << "Compositing forced to OpenGL mode by environment variable"; compositingMode = OpenGLCompositing; useCompositing = true; break; case 'X': qCDebug(KWIN_CORE) << "Compositing forced to XRender mode by environment variable"; compositingMode = XRenderCompositing; useCompositing = true; break; case 'Q': qCDebug(KWIN_CORE) << "Compositing forced to QPainter mode by environment variable"; compositingMode = QPainterCompositing; useCompositing = true; break; case 'N': if (getenv("KDE_FAILSAFE")) qCDebug(KWIN_CORE) << "Compositing disabled forcefully by KDE failsafe mode"; else qCDebug(KWIN_CORE) << "Compositing disabled forcefully by environment variable"; compositingMode = NoCompositing; break; default: qCDebug(KWIN_CORE) << "Unknown KWIN_COMPOSE mode set, ignoring"; break; } } if (kwinApp()->shouldUseWaylandForCompositing() && (compositingMode == XRenderCompositing || compositingMode == NoCompositing)) { qCDebug(KWIN_CORE) << "Compositing forced to QPainter mode by invalid compositor selection"; compositingMode = QPainterCompositing; } setCompositingMode(compositingMode); if (m_compositingMode == NoCompositing) { setUseCompositing(false); return false; // do not even detect compositing preferences if explicitly disabled } // it's either enforced by env or by initial resume from "suspend" or we check the settings setUseCompositing(useCompositing || force || config.readEntry("Enabled", Options::defaultUseCompositing())); if (!m_useCompositing) return false; // not enforced or necessary and not "enabled" by settings return true; } void Options::reloadCompositingSettings(bool force) { if (!loadCompositingConfig(force)) { return; } // from now on we've an initial setup and don't have to reload settings on compositing activation // see Workspace::setupCompositing(), composite.cpp setCompositingInitialized(true); // Compositing settings KConfigGroup config(m_settings->config(), "Compositing"); setGlSmoothScale(qBound(-1, config.readEntry("GLTextureFilter", Options::defaultGlSmoothScale()), 2)); setGlStrictBindingFollowsDriver(!config.hasKey("GLStrictBinding")); if (!isGlStrictBindingFollowsDriver()) { setGlStrictBinding(config.readEntry("GLStrictBinding", Options::defaultGlStrictBinding())); } setGLCoreProfile(config.readEntry("GLCore", Options::defaultGLCoreProfile())); char c = 0; const QString s = config.readEntry("GLPreferBufferSwap", QString(Options::defaultGlPreferBufferSwap())); if (!s.isEmpty()) c = s.at(0).toAscii(); if (c != 'a' && c != 'c' && c != 'p' && c != 'e') c = 0; setGlPreferBufferSwap(c); setColorCorrected(config.readEntry("GLColorCorrection", Options::defaultColorCorrected())); m_xrenderSmoothScale = config.readEntry("XRenderSmoothScale", false); HiddenPreviews previews = Options::defaultHiddenPreviews(); // 4 - off, 5 - shown, 6 - always, other are old values int hps = config.readEntry("HiddenPreviews", 5); if (hps == 4) previews = HiddenPreviewsNever; else if (hps == 5) previews = HiddenPreviewsShown; else if (hps == 6) previews = HiddenPreviewsAlways; setHiddenPreviews(previews); setUnredirectFullscreen(config.readEntry("UnredirectFullscreen", Options::defaultUnredirectFullscreen())); // TOOD: add setter animationSpeed = qBound(0, config.readEntry("AnimationSpeed", Options::defaultAnimationSpeed()), 6); auto interfaceToKey = [](OpenGLPlatformInterface interface) { switch (interface) { case GlxPlatformInterface: return QStringLiteral("glx"); case EglPlatformInterface: return QStringLiteral("egl"); default: return QString(); } }; auto keyToInterface = [](const QString &key) { if (key == QStringLiteral("glx")) { return GlxPlatformInterface; } else if (key == QStringLiteral("egl")) { return EglPlatformInterface; } return defaultGlPlatformInterface(); }; setGlPlatformInterface(keyToInterface(config.readEntry("GLPlatformInterface", interfaceToKey(m_glPlatformInterface)))); } // restricted should be true for operations that the user may not be able to repeat // if the window is moved out of the workspace (e.g. if the user moves a window // by the titlebar, and moves it too high beneath Kicker at the top edge, they // may not be able to move it back, unless they know about Alt+LMB) Options::WindowOperation Options::windowOperation(const QString &name, bool restricted) { if (name == QStringLiteral("Move")) return restricted ? MoveOp : UnrestrictedMoveOp; else if (name == QStringLiteral("Resize")) return restricted ? ResizeOp : UnrestrictedResizeOp; else if (name == QStringLiteral("Maximize")) return MaximizeOp; else if (name == QStringLiteral("Minimize")) return MinimizeOp; else if (name == QStringLiteral("Close")) return CloseOp; else if (name == QStringLiteral("OnAllDesktops")) return OnAllDesktopsOp; else if (name == QStringLiteral("Shade")) return ShadeOp; else if (name == QStringLiteral("Operations")) return OperationsOp; else if (name == QStringLiteral("Maximize (vertical only)")) return VMaximizeOp; else if (name == QStringLiteral("Maximize (horizontal only)")) return HMaximizeOp; else if (name == QStringLiteral("Lower")) return LowerOp; return NoOp; } Options::MouseCommand Options::mouseCommand(const QString &name, bool restricted) { QString lowerName = name.toLower(); if (lowerName == QStringLiteral("raise")) return MouseRaise; if (lowerName == QStringLiteral("lower")) return MouseLower; if (lowerName == QStringLiteral("operations menu")) return MouseOperationsMenu; if (lowerName == QStringLiteral("toggle raise and lower")) return MouseToggleRaiseAndLower; if (lowerName == QStringLiteral("activate and raise")) return MouseActivateAndRaise; if (lowerName == QStringLiteral("activate and lower")) return MouseActivateAndLower; if (lowerName == QStringLiteral("activate")) return MouseActivate; if (lowerName == QStringLiteral("activate, raise and pass click")) return MouseActivateRaiseAndPassClick; if (lowerName == QStringLiteral("activate and pass click")) return MouseActivateAndPassClick; if (lowerName == QStringLiteral("scroll")) return MouseNothing; if (lowerName == QStringLiteral("activate and scroll")) return MouseActivateAndPassClick; if (lowerName == QStringLiteral("activate, raise and scroll")) return MouseActivateRaiseAndPassClick; if (lowerName == QStringLiteral("activate, raise and move")) return restricted ? MouseActivateRaiseAndMove : MouseActivateRaiseAndUnrestrictedMove; if (lowerName == QStringLiteral("move")) return restricted ? MouseMove : MouseUnrestrictedMove; if (lowerName == QStringLiteral("resize")) return restricted ? MouseResize : MouseUnrestrictedResize; if (lowerName == QStringLiteral("shade")) return MouseShade; if (lowerName == QStringLiteral("minimize")) return MouseMinimize; if (lowerName == QStringLiteral("start window tab drag")) return MouseDragTab; if (lowerName == QStringLiteral("close")) return MouseClose; if (lowerName == QStringLiteral("increase opacity")) return MouseOpacityMore; if (lowerName == QStringLiteral("decrease opacity")) return MouseOpacityLess; if (lowerName == QStringLiteral("nothing")) return MouseNothing; return MouseNothing; } Options::MouseWheelCommand Options::mouseWheelCommand(const QString &name) { QString lowerName = name.toLower(); if (lowerName == QStringLiteral("raise/lower")) return MouseWheelRaiseLower; if (lowerName == QStringLiteral("shade/unshade")) return MouseWheelShadeUnshade; if (lowerName == QStringLiteral("maximize/restore")) return MouseWheelMaximizeRestore; if (lowerName == QStringLiteral("above/below")) return MouseWheelAboveBelow; if (lowerName == QStringLiteral("previous/next desktop")) return MouseWheelPreviousNextDesktop; if (lowerName == QStringLiteral("change opacity")) return MouseWheelChangeOpacity; if (lowerName == QStringLiteral("switch to window tab to the left/right")) return MouseWheelChangeCurrentTab; if (lowerName == QStringLiteral("nothing")) return MouseWheelNothing; return MouseWheelChangeCurrentTab; } bool Options::showGeometryTip() const { return show_geometry_tip; } bool Options::condensedTitle() const { return condensed_title; } Options::MouseCommand Options::wheelToMouseCommand(MouseWheelCommand com, int delta) const { switch(com) { case MouseWheelRaiseLower: return delta > 0 ? MouseRaise : MouseLower; case MouseWheelShadeUnshade: return delta > 0 ? MouseSetShade : MouseUnsetShade; case MouseWheelMaximizeRestore: return delta > 0 ? MouseMaximize : MouseRestore; case MouseWheelAboveBelow: return delta > 0 ? MouseAbove : MouseBelow; case MouseWheelPreviousNextDesktop: return delta > 0 ? MousePreviousDesktop : MouseNextDesktop; case MouseWheelChangeOpacity: return delta > 0 ? MouseOpacityMore : MouseOpacityLess; case MouseWheelChangeCurrentTab: return delta > 0 ? MousePreviousTab : MouseNextTab; default: return MouseNothing; } } #endif double Options::animationTimeFactor() const { const double factors[] = { 0, 0.2, 0.5, 1, 2, 4, 20 }; return factors[ animationSpeed ]; } Options::WindowOperation Options::operationMaxButtonClick(Qt::MouseButtons button) const { return button == Qt::RightButton ? opMaxButtonRightClick : button == Qt::MidButton ? opMaxButtonMiddleClick : opMaxButtonLeftClick; } QStringList Options::modifierOnlyDBusShortcut(Qt::KeyboardModifier mod) const { return m_modifierOnlyShortcuts.value(mod); } bool Options::isUseCompositing() const { return m_useCompositing || kwinApp()->platform()->requiresCompositing(); } bool Options::isUnredirectFullscreen() const { return m_unredirectFullscreen && !kwinApp()->platform()->requiresCompositing(); } } // namespace diff --git a/platform.cpp b/platform.cpp index 1273df848..736e38446 100644 --- a/platform.cpp +++ b/platform.cpp @@ -1,289 +1,304 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 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 "platform.h" #include #include "abstract_egl_backend.h" #include "composite.h" #include "cursor.h" #include "input.h" #include "pointer_input.h" #include "scene_opengl.h" #include "screenedge.h" #include "wayland_server.h" namespace KWin { Platform::Platform(QObject *parent) : QObject(parent) { } Platform::~Platform() { } QImage Platform::softwareCursor() const { return input()->pointer()->cursorImage(); } QPoint Platform::softwareCursorHotspot() const { return input()->pointer()->cursorHotSpot(); } Screens *Platform::createScreens(QObject *parent) { Q_UNUSED(parent) return nullptr; } OpenGLBackend *Platform::createOpenGLBackend() { return nullptr; } QPainterBackend *Platform::createQPainterBackend() { return nullptr; } Edge *Platform::createScreenEdge(ScreenEdges *edges) { return new Edge(edges); } void Platform::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config) { Q_UNUSED(config) qCWarning(KWIN_CORE) << "This backend does not support configuration changes."; } void Platform::setSoftWareCursor(bool set) { if (m_softWareCursor == set) { return; } m_softWareCursor = set; if (m_softWareCursor) { connect(Cursor::self(), &Cursor::posChanged, this, &Platform::triggerCursorRepaint); } else { disconnect(Cursor::self(), &Cursor::posChanged, this, &Platform::triggerCursorRepaint); } } void Platform::triggerCursorRepaint() { if (!Compositor::self()) { return; } const QPoint &hotSpot = softwareCursorHotspot(); const QSize &size = softwareCursor().size(); Compositor::self()->addRepaint(m_cursor.lastRenderedPosition.x() - hotSpot.x(), m_cursor.lastRenderedPosition.y() - hotSpot.y(), size.width(), size.height()); } void Platform::markCursorAsRendered() { if (m_softWareCursor) { m_cursor.lastRenderedPosition = Cursor::pos(); } if (input()->pointer()) { input()->pointer()->markCursorAsRendered(); } } void Platform::keyboardKeyPressed(quint32 key, quint32 time) { if (!input()) { return; } input()->processKeyboardKey(key, InputRedirection::KeyboardKeyPressed, time); } void Platform::keyboardKeyReleased(quint32 key, quint32 time) { if (!input()) { return; } input()->processKeyboardKey(key, InputRedirection::KeyboardKeyReleased, time); } void Platform::keyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) { if (!input()) { return; } input()->processKeyboardModifiers(modsDepressed, modsLatched, modsLocked, group); } void Platform::keymapChange(int fd, uint32_t size) { if (!input()) { return; } input()->processKeymapChange(fd, size); } void Platform::pointerAxisHorizontal(qreal delta, quint32 time) { if (!input()) { return; } input()->processPointerAxis(InputRedirection::PointerAxisHorizontal, delta, time); } void Platform::pointerAxisVertical(qreal delta, quint32 time) { if (!input()) { return; } input()->processPointerAxis(InputRedirection::PointerAxisVertical, delta, time); } void Platform::pointerButtonPressed(quint32 button, quint32 time) { if (!input()) { return; } input()->processPointerButton(button, InputRedirection::PointerButtonPressed, time); } void Platform::pointerButtonReleased(quint32 button, quint32 time) { if (!input()) { return; } input()->processPointerButton(button, InputRedirection::PointerButtonReleased, time); } void Platform::pointerMotion(const QPointF &position, quint32 time) { if (!input()) { return; } input()->processPointerMotion(position, time); } void Platform::touchCancel() { if (!input()) { return; } input()->cancelTouch(); } void Platform::touchDown(qint32 id, const QPointF &pos, quint32 time) { if (!input()) { return; } input()->processTouchDown(id, pos, time); } void Platform::touchFrame() { if (!input()) { return; } input()->touchFrame(); } void Platform::touchMotion(qint32 id, const QPointF &pos, quint32 time) { if (!input()) { return; } input()->processTouchMotion(id, pos, time); } void Platform::touchUp(qint32 id, quint32 time) { if (!input()) { return; } input()->processTouchUp(id, time); } void Platform::repaint(const QRect &rect) { if (!Compositor::self()) { return; } Compositor::self()->addRepaint(rect); } void Platform::setReady(bool ready) { if (m_ready == ready) { return; } m_ready = ready; emit readyChanged(m_ready); } void Platform::warpPointer(const QPointF &globalPos) { Q_UNUSED(globalPos) } bool Platform::supportsQpaContext() const { return hasGLExtension(QByteArrayLiteral("EGL_KHR_surfaceless_context")); } EGLDisplay Platform::sceneEglDisplay() const { if (Compositor *c = Compositor::self()) { if (SceneOpenGL *s = dynamic_cast(c->scene())) { return static_cast(s->backend())->eglDisplay(); } } return EGL_NO_DISPLAY; } EGLContext Platform::sceneEglContext() const { if (Compositor *c = Compositor::self()) { if (SceneOpenGL *s = dynamic_cast(c->scene())) { return static_cast(s->backend())->context(); } } return EGL_NO_CONTEXT; } QSize Platform::screenSize() const { return QSize(); } QVector Platform::screenGeometries() const { return QVector({QRect(QPoint(0, 0), screenSize())}); } bool Platform::requiresCompositing() const { return true; } +bool Platform::compositingPossible() const +{ + return true; +} + +QString Platform::compositingNotPossibleReason() const +{ + return QString(); +} + +bool Platform::openGLCompositingIsBroken() const +{ + return false; +} + } diff --git a/platform.h b/platform.h index 036874268..b865afc86 100644 --- a/platform.h +++ b/platform.h @@ -1,202 +1,226 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 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 . *********************************************************************/ #ifndef KWIN_PLATFORM_H #define KWIN_PLATFORM_H #include #include #include #include #include namespace KWayland { namespace Server { class OutputConfigurationInterface; } } namespace KWin { class Edge; class OpenGLBackend; class QPainterBackend; class Screens; class ScreenEdges; class WaylandCursorTheme; class KWIN_EXPORT Platform : public QObject { Q_OBJECT public: virtual ~Platform(); virtual void init() = 0; virtual Screens *createScreens(QObject *parent = nullptr); virtual OpenGLBackend *createOpenGLBackend(); virtual QPainterBackend *createQPainterBackend(); /** * Allows the platform to create a platform specific screen edge. * The default implementation creates a Edge. **/ virtual Edge *createScreenEdge(ScreenEdges *parent); virtual void warpPointer(const QPointF &globalPos); /** * Whether our Compositing EGL display allows a surface less context * so that a sharing context could be created. **/ virtual bool supportsQpaContext() const; /** * The EGLDisplay used by the compositing scene. **/ virtual EGLDisplay sceneEglDisplay() const; /** * The EGLContext used by the compositing scene. **/ virtual EGLContext sceneEglContext() const; /** * Implementing subclasses should provide a size in case the backend represents * a basic screen and uses the BasicScreens. * * Base implementation returns an invalid size. **/ virtual QSize screenSize() const; /** * Implementing subclasses should provide all geometries in case the backend represents * a basic screen and uses the BasicScreens. * * Base implementation returns one QRect positioned at 0/0 with screenSize() as size. **/ virtual QVector screenGeometries() const; /** * Implement this method to receive configuration change requests through KWayland's * OutputManagement interface. * * Base implementation warns that the current backend does not implement this * functionality. */ virtual void configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config); /** * Whether the Platform requires compositing for rendering. * Default implementation returns @c true. If the implementing Platform allows to be used * without compositing (e.g. rendering is done by the windowing system), re-implement this method. **/ virtual bool requiresCompositing() const; + /** + * Whether Compositing is possible in the Platform. + * Returning @c false in this method makes only sense if @link{requiresCompositing} returns @c false. + * + * The default implementation returns @c true. + * @see requiresCompositing + **/ + virtual bool compositingPossible() const; + /** + * Returns a user facing text explaining why compositing is not possible in case + * @link{compositingPossible} returns @c false. + * + * The default implementation returns an empty string. + * @see compositingPossible + **/ + virtual QString compositingNotPossibleReason() const; + /** + * Whether OpenGL compositing is broken. + * The Platform can implement this method if it is able to detect whether OpenGL compositing + * broke (e.g. triggered a crash in a previous run). + * + * Default implementation returns @c false. + **/ + virtual bool openGLCompositingIsBroken() const; bool usesSoftwareCursor() const { return m_softWareCursor; } QImage softwareCursor() const; QPoint softwareCursorHotspot() const; void markCursorAsRendered(); bool handlesOutputs() const { return m_handlesOutputs; } bool isReady() const { return m_ready; } void setInitialWindowSize(const QSize &size) { m_initialWindowSize = size; } void setDeviceIdentifier(const QByteArray &identifier) { m_deviceIdentifier = identifier; } bool supportsPointerWarping() const { return m_pointerWarping; } bool areOutputsEnabled() const { return m_outputsEnabled; } void setOutputsEnabled(bool enabled) { m_outputsEnabled = enabled; } int initialOutputCount() const { return m_initialOutputCount; } void setInitialOutputCount(int count) { m_initialOutputCount = count; } public Q_SLOTS: void pointerMotion(const QPointF &position, quint32 time); void pointerButtonPressed(quint32 button, quint32 time); void pointerButtonReleased(quint32 button, quint32 time); void pointerAxisHorizontal(qreal delta, quint32 time); void pointerAxisVertical(qreal delta, quint32 time); void keyboardKeyPressed(quint32 key, quint32 time); void keyboardKeyReleased(quint32 key, quint32 time); void keyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); void keymapChange(int fd, uint32_t size); void touchDown(qint32 id, const QPointF &pos, quint32 time); void touchUp(qint32 id, quint32 time); void touchMotion(qint32 id, const QPointF &pos, quint32 time); void touchCancel(); void touchFrame(); Q_SIGNALS: void screensQueried(); void initFailed(); void cursorChanged(); void readyChanged(bool); /** * Emitted by backends using a one screen (nested window) approach and when the size of that changes. **/ void screenSizeChanged(); protected: explicit Platform(QObject *parent = nullptr); void setSoftWareCursor(bool set); void handleOutputs() { m_handlesOutputs = true; } void repaint(const QRect &rect); void setReady(bool ready); QSize initialWindowSize() const { return m_initialWindowSize; } QByteArray deviceIdentifier() const { return m_deviceIdentifier; } void setSupportsPointerWarping(bool set) { m_pointerWarping = set; } private: void triggerCursorRepaint(); bool m_softWareCursor = false; struct { QPoint lastRenderedPosition; } m_cursor; bool m_handlesOutputs = false; bool m_ready = false; QSize m_initialWindowSize; QByteArray m_deviceIdentifier; bool m_pointerWarping = false; bool m_outputsEnabled = true; int m_initialOutputCount = 1; }; } Q_DECLARE_INTERFACE(KWin::Platform, "org.kde.kwin.Platform") #endif diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp index fa090a886..269842612 100644 --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -1,83 +1,158 @@ /******************************************************************** 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 "x11_platform.h" #include "edge.h" #include #if HAVE_EPOXY_GLX #include "glxbackend.h" #endif #include "eglonxbackend.h" #include "screens_xrandr.h" #include "options.h" +#include +#include + +#include #include namespace KWin { X11StandalonePlatform::X11StandalonePlatform(QObject *parent) : Platform(parent) { } X11StandalonePlatform::~X11StandalonePlatform() = default; void X11StandalonePlatform::init() { if (!QX11Info::isPlatformX11()) { emit initFailed(); return; } setReady(true); emit screensQueried(); } Screens *X11StandalonePlatform::createScreens(QObject *parent) { return new XRandRScreens(parent); } OpenGLBackend *X11StandalonePlatform::createOpenGLBackend() { switch (options->glPlatformInterface()) { #if HAVE_EPOXY_GLX case GlxPlatformInterface: return new GlxBackend(); #endif case EglPlatformInterface: return new EglOnXBackend(); default: // no backend available return nullptr; } } Edge *X11StandalonePlatform::createScreenEdge(ScreenEdges *edges) { return new WindowBasedEdge(edges); } bool X11StandalonePlatform::requiresCompositing() const { return false; } +bool X11StandalonePlatform::openGLCompositingIsBroken() const +{ + const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); + return KConfigGroup(kwinApp()->config(), "Compositing").readEntry(unsafeKey, false); +} + +QString X11StandalonePlatform::compositingNotPossibleReason() const +{ + // first off, check whether we figured that we'll crash on detection because of a buggy driver + KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); + const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); + if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && + gl_workaround_group.readEntry(unsafeKey, false)) + return i18n("OpenGL compositing (the default) has crashed KWin in the past.
" + "This was most likely due to a driver bug." + "

If you think that you have meanwhile upgraded to a stable driver,
" + "you can reset this protection but be aware that this might result in an immediate crash!

" + "

Alternatively, you might want to use the XRender backend instead.

"); + + if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) { + return i18n("Required X extensions (XComposite and XDamage) are not available."); + } +#if !defined( KWIN_HAVE_XRENDER_COMPOSITING ) + if (!hasGlx()) + return i18n("GLX/OpenGL are not available and only OpenGL support is compiled."); +#else + if (!(hasGlx() + || (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable()))) { + return i18n("GLX/OpenGL and XRender/XFixes are not available."); + } +#endif + return QString(); +} + +bool X11StandalonePlatform::compositingPossible() const +{ + // first off, check whether we figured that we'll crash on detection because of a buggy driver + KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); + const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); + if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && + gl_workaround_group.readEntry(unsafeKey, false)) + return false; + + + if (!Xcb::Extensions::self()->isCompositeAvailable()) { + qCDebug(KWIN_CORE) << "No composite extension available"; + return false; + } + if (!Xcb::Extensions::self()->isDamageAvailable()) { + qCDebug(KWIN_CORE) << "No damage extension available"; + return false; + } + if (hasGlx()) + return true; +#ifdef KWIN_HAVE_XRENDER_COMPOSITING + if (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable()) + return true; +#endif + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + return true; + } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { + return true; + } + qCDebug(KWIN_CORE) << "No OpenGL or XRender/XFixes support"; + return false; +} + +bool X11StandalonePlatform::hasGlx() +{ + return Xcb::Extensions::self()->hasGlx(); +} } diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h index 949f804ca..dd6c71b6b 100644 --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -1,50 +1,65 @@ /******************************************************************** 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 . *********************************************************************/ #ifndef KWIN_X11STANDALONE_PLATFORM_H #define KWIN_X11STANDALONE_PLATFORM_H #include "platform.h" #include #include namespace KWin { class KWIN_EXPORT X11StandalonePlatform : public Platform { Q_OBJECT Q_INTERFACES(KWin::Platform) Q_PLUGIN_METADATA(IID "org.kde.kwin.Platform" FILE "x11.json") public: X11StandalonePlatform(QObject *parent = nullptr); virtual ~X11StandalonePlatform(); void init() override; Screens *createScreens(QObject *parent = nullptr) override; OpenGLBackend *createOpenGLBackend() override; Edge *createScreenEdge(ScreenEdges *parent) override; bool requiresCompositing() const override; + bool compositingPossible() const override; + QString compositingNotPossibleReason() const override; + bool openGLCompositingIsBroken() const override; + +private: + /** + * Tests whether GLX is supported and returns @c true + * in case KWin is compiled with OpenGL support and GLX + * is available. + * + * If KWin is compiled with OpenGL ES or without OpenGL at + * all, @c false is returned. + * @returns @c true if GLX is available, @c false otherwise and if not build with OpenGL support. + **/ + static bool hasGlx(); }; } #endif