diff --git a/CMakeLists.txt b/CMakeLists.txt index 2de1a9a6a..d31a9abf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,752 +1,753 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(KWIN) set(PROJECT_VERSION "5.16.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.12.0") set(KF5_MIN_VERSION "5.58.0") set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ) find_package(ECM 5.38 REQUIRED NO_MODULE) include(FeatureSummary) include(WriteBasicConfigVersionFile) include(GenerateExportHeader) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Concurrent Core DBus Quick QuickWidgets Sensors Script UiTools Widgets X11Extras ) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG QUIET) set_package_properties(Qt5Test PROPERTIES PURPOSE "Required for tests" TYPE OPTIONAL ) add_feature_info("Qt5Test" Qt5Test_FOUND "Required for building tests") if (NOT Qt5Test_FOUND) set(BUILD_TESTING OFF CACHE BOOL "Build the testing tree.") endif() include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMOptionalAddSubdirectory) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0 -DQT_USE_QSTRINGBUILDER -DQT_NO_URL_CAST_FROM_STRING) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # This is a workaround/compromise for a Kwin specific policy of not applying the relevant override fix. # See thread in D18167. if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-suggest-override") endif() find_package(Qt5Multimedia QUIET) set_package_properties(Qt5Multimedia PROPERTIES PURPOSE "Runtime-only dependency for effect video playback" TYPE RUNTIME ) # required frameworks by Core find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Config ConfigWidgets CoreAddons Crash GlobalAccel I18n Init Notifications Package Plasma WidgetsAddons WindowSystem IconThemes IdleTime Wayland ) # required frameworks by config modules find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Completion Declarative KCMUtils KIO TextWidgets NewStuff Service XmlGui ) find_package(Threads) set_package_properties(Threads PROPERTIES PURPOSE "Needed for VirtualTerminal support in KWin Wayland" TYPE REQUIRED ) # optional frameworks find_package(KF5Activities ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5Activities PROPERTIES PURPOSE "Enable building of KWin with kactivities support" TYPE OPTIONAL ) add_feature_info("KF5Activities" KF5Activities_FOUND "Enable building of KWin with kactivities support") find_package(KF5DocTools ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5DocTools PROPERTIES PURPOSE "Enable building documentation" TYPE OPTIONAL ) add_feature_info("KF5DocTools" KF5DocTools_FOUND "Enable building documentation") find_package(KF5Kirigami2 ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5Kirigami2 PROPERTIES DESCRIPTION "A QtQuick based components set" PURPOSE "Required at runtime for Virtual desktop KCM and the virtual keyboard" TYPE RUNTIME ) find_package(KDecoration2 5.13.0 CONFIG REQUIRED) find_package(KScreenLocker CONFIG REQUIRED) set_package_properties(KScreenLocker PROPERTIES TYPE REQUIRED PURPOSE "For screenlocker integration in kwin_wayland") find_package(Breeze 5.9.0 CONFIG) set_package_properties(Breeze PROPERTIES TYPE OPTIONAL PURPOSE "For setting the default window decoration plugin") if(${Breeze_FOUND}) if(${BREEZE_WITH_KDECORATION}) set(HAVE_BREEZE_DECO true) else() set(HAVE_BREEZE_DECO FALSE) endif() else() set(HAVE_BREEZE_DECO FALSE) endif() add_feature_info("Breeze-Decoration" HAVE_BREEZE_DECO "Default decoration plugin Breeze") find_package(EGL) set_package_properties(EGL PROPERTIES TYPE RUNTIME PURPOSE "Required to build KWin with EGL support" ) find_package(epoxy) set_package_properties(epoxy PROPERTIES DESCRIPTION "libepoxy" URL "https://github.com/anholt/libepoxy" TYPE REQUIRED PURPOSE "OpenGL dispatch library" ) set(HAVE_DL_LIBRARY FALSE) if(epoxy_HAS_GLX) find_library(DL_LIBRARY dl) if(DL_LIBRARY) set(HAVE_DL_LIBRARY TRUE) endif() endif() find_package(Wayland 1.2 REQUIRED COMPONENTS Cursor OPTIONAL_COMPONENTS Egl) set_package_properties(Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" ) add_feature_info("Wayland::EGL" Wayland_Egl_FOUND "Enable building of Wayland backend and QPA with EGL support.") set(HAVE_WAYLAND_EGL FALSE) if(Wayland_Egl_FOUND) set(HAVE_WAYLAND_EGL TRUE) endif() find_package(XKB 0.7.0) set_package_properties(XKB PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" ) find_package(Libinput 1.9) set_package_properties(Libinput PROPERTIES TYPE REQUIRED PURPOSE "Required for input handling on Wayland.") find_package(UDev) set_package_properties(UDev PROPERTIES URL "http://www.freedesktop.org/software/systemd/libudev/" DESCRIPTION "Linux device library." TYPE REQUIRED PURPOSE "Required for input handling on Wayland." ) find_package(Libdrm 2.4.62) set_package_properties(Libdrm PROPERTIES TYPE OPTIONAL PURPOSE "Required for drm output on Wayland.") set(HAVE_DRM FALSE) if(Libdrm_FOUND) set(HAVE_DRM TRUE) endif() find_package(gbm) set_package_properties(gbm PROPERTIES TYPE OPTIONAL PURPOSE "Required for egl output of drm backend.") set(HAVE_GBM FALSE) if(HAVE_DRM AND gbm_FOUND) set(HAVE_GBM TRUE) endif() option(KWIN_BUILD_EGL_STREAM_BACKEND "Enable building of EGLStream based DRM backend" ON) if(HAVE_DRM AND KWIN_BUILD_EGL_STREAM_BACKEND) set(HAVE_EGL_STREAMS 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 "https://www.x.org" TYPE REQUIRED ) add_feature_info("XInput" X11_Xinput_FOUND "Required for poll-free mouse cursor updates") set(HAVE_X11_XINPUT ${X11_Xinput_FOUND}) # All the required XCB components find_package(XCB 1.10 REQUIRED COMPONENTS XCB XFIXES DAMAGE COMPOSITE SHAPE SYNC RENDER RANDR KEYSYMS IMAGE SHM GLX CURSOR ICCCM ) set_package_properties(XCB PROPERTIES TYPE REQUIRED) # and the optional XCB dependencies if (XCB_ICCCM_VERSION VERSION_LESS "0.4") set(XCB_ICCCM_FOUND FALSE) endif() add_feature_info("XCB-ICCCM" XCB_ICCCM_FOUND "Required for building test applications for KWin") find_package(X11_XCB) set_package_properties(X11_XCB PROPERTIES PURPOSE "Required for building X11 windowed backend of kwin_wayland" TYPE OPTIONAL) # dependencies for QPA plugin find_package(Qt5FontDatabaseSupport REQUIRED) find_package(Qt5ThemeSupport REQUIRED) find_package(Qt5EventDispatcherSupport REQUIRED) find_package(Freetype REQUIRED) set_package_properties(Freetype PROPERTIES DESCRIPTION "A font rendering engine" URL "https://www.freetype.org" TYPE REQUIRED PURPOSE "Needed for KWin's QPA plugin." ) find_package(Fontconfig REQUIRED) set_package_properties(Fontconfig PROPERTIES TYPE REQUIRED PURPOSE "Needed for KWin's QPA plugin." ) find_package(Xwayland) set_package_properties(Xwayland PROPERTIES URL "https://x.org" DESCRIPTION "Xwayland X server" TYPE RUNTIME PURPOSE "Needed for running kwin_wayland" ) find_package(Libcap) set_package_properties(Libcap PROPERTIES TYPE OPTIONAL PURPOSE "Needed for running kwin_wayland with real-time scheduling policy" ) set(HAVE_LIBCAP ${Libcap_FOUND}) include(ECMQMLModules) ecm_find_qmlmodule(QtQuick 2.3) ecm_find_qmlmodule(QtQuick.Controls 1.2) ecm_find_qmlmodule(QtQuick.Layouts 1.3) ecm_find_qmlmodule(QtQuick.VirtualKeyboard 2.1) ecm_find_qmlmodule(QtQuick.Window 2.1) ecm_find_qmlmodule(QtMultimedia 5.0) ecm_find_qmlmodule(org.kde.kquickcontrolsaddons 2.0) ecm_find_qmlmodule(org.kde.plasma.core 2.0) ecm_find_qmlmodule(org.kde.plasma.components 2.0) ########### configure tests ############### include(CMakeDependentOption) option(KWIN_BUILD_DECORATIONS "Enable building of KWin decorations." ON) option(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON) option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON) option(KWIN_BUILD_XRENDER_COMPOSITING "Enable building of KWin with XRender Compositing support" ON) cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "KF5Activities_FOUND" OFF) # Binary name of KWin set(KWIN_NAME "kwin") set(KWIN_INTERNAL_NAME_X11 "kwin_x11") set(KWIN_INTERNAL_NAME_WAYLAND "kwin_wayland") set(KWIN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # KWIN_HAVE_XRENDER_COMPOSITING - whether XRender-based compositing support is available: may be disabled if( KWIN_BUILD_XRENDER_COMPOSITING ) set( KWIN_HAVE_XRENDER_COMPOSITING 1 ) endif() include_directories(${XKB_INCLUDE_DIR}) include_directories(${epoxy_INCLUDE_DIR}) set(HAVE_EPOXY_GLX ${epoxy_HAS_GLX}) # for things that are also used by kwin libraries configure_file(libkwineffects/kwinconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects/kwinconfig.h ) # for kwin internal things set(HAVE_X11_XCB ${X11_XCB_FOUND}) include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckSymbolExists) check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(malloc.h HAVE_MALLOC_H) check_include_file("sys/prctl.h" HAVE_SYS_PRCTL_H) check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE) check_symbol_exists(PR_SET_PDEATHSIG "sys/prctl.h" HAVE_PR_SET_PDEATHSIG) check_include_file("sys/procctl.h" HAVE_SYS_PROCCTL_H) check_symbol_exists(PROC_TRACE_CTL "sys/procctl.h" HAVE_PROC_TRACE_CTL) if (HAVE_PR_SET_DUMPABLE OR HAVE_PROC_TRACE_CTL) set(CAN_DISABLE_PTRACE TRUE) endif() add_feature_info("prctl/procctl tracing control" CAN_DISABLE_PTRACE "Required for disallowing ptrace on kwin_wayland process") check_include_file("sys/sysmacros.h" HAVE_SYS_SYSMACROS_H) configure_file(config-kwin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwin.h ) check_include_file("linux/vt.h" HAVE_LINUX_VT_H) add_feature_info("linux/vt.h" HAVE_LINUX_VT_H "Required for virtual terminal support under wayland") check_include_file("linux/fb.h" HAVE_LINUX_FB_H) add_feature_info("linux/fb.h" HAVE_LINUX_FB_H "Required for the fbdev backend") check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK) add_feature_info("SCHED_RESET_ON_FORK" HAVE_SCHED_RESET_ON_FORK "Required for running kwin_wayland with real-time scheduling") ########### global ############### set(kwin_effects_dbus_xml ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.kwin.Effects.xml) include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libkwineffects ${CMAKE_CURRENT_SOURCE_DIR}/effects ${CMAKE_CURRENT_SOURCE_DIR}/tabbox ${CMAKE_CURRENT_SOURCE_DIR}/platformsupport ) add_subdirectory( libkwineffects ) if(KWIN_BUILD_KCMS) add_subdirectory( kcmkwin ) endif() add_subdirectory( data ) add_subdirectory( effects ) add_subdirectory( scripts ) add_subdirectory( tabbox ) add_subdirectory(scripting) add_subdirectory(helpers) ########### next target ############### set(kwin_KDEINIT_SRCS workspace.cpp dbusinterface.cpp virtualdesktopsdbustypes.cpp abstract_client.cpp client.cpp client_machine.cpp cursor.cpp debug_console.cpp tabgroup.cpp focuschain.cpp globalshortcuts.cpp input.cpp input_event.cpp input_event_spy.cpp keyboard_input.cpp keyboard_layout.cpp keyboard_layout_switching.cpp keyboard_repeat.cpp pointer_input.cpp touch_input.cpp netinfo.cpp placement.cpp atoms.cpp utils.cpp layers.cpp main.cpp options.cpp outline.cpp events.cpp killwindow.cpp geometrytip.cpp screens.cpp outputscreens.cpp shadow.cpp sm.cpp group.cpp manage.cpp overlaywindow.cpp activation.cpp useractions.cpp geometry.cpp rules.cpp composite.cpp toplevel.cpp unmanaged.cpp scene.cpp screenlockerwatcher.cpp thumbnailitem.cpp deleted.cpp effects.cpp effectloader.cpp virtualdesktops.cpp xcbutils.cpp x11eventfilter.cpp logind.cpp onscreennotification.cpp osd.cpp screenedge.cpp scripting/scripting.cpp scripting/workspace_wrapper.cpp scripting/meta.cpp scripting/scriptedeffect.cpp scripting/scriptingutils.cpp scripting/timer.cpp scripting/scripting_model.cpp scripting/dbuscall.cpp scripting/screenedgeitem.cpp scripting/scripting_logging.cpp decorations/decoratedclient.cpp decorations/decorationbridge.cpp decorations/decorationpalette.cpp decorations/settings.cpp decorations/decorationrenderer.cpp decorations/decorations_logging.cpp platform.cpp abstract_output.cpp + abstract_wayland_output.cpp shell_client.cpp wayland_server.cpp wayland_cursor_theme.cpp virtualkeyboard.cpp virtualkeyboard_dbus.cpp appmenu.cpp modifier_only_shortcuts.cpp xkb.cpp gestures.cpp popup_input_filter.cpp colorcorrection/manager.cpp colorcorrection/colorcorrectdbusinterface.cpp colorcorrection/suncalc.cpp abstract_opengl_context_attribute_builder.cpp egl_context_attribute_builder.cpp was_user_interaction_x11_filter.cpp moving_client_x11_filter.cpp window_property_notify_x11_filter.cpp rootinfo_filter.cpp orientation_sensor.cpp idle_inhibition.cpp libinput/context.cpp libinput/connection.cpp libinput/device.cpp libinput/events.cpp libinput/libinput_logging.cpp udev.cpp touch_hide_cursor_spy.cpp internal_client.cpp xwl/xwayland_interface.cpp ) include(ECMQtDeclareLoggingCategory) ecm_qt_declare_logging_category(kwin_KDEINIT_SRCS HEADER colorcorrect_logging.h IDENTIFIER KWIN_COLORCORRECTION CATEGORY_NAME kwin_colorcorrection DEFAULT_SEVERITY Critical ) if(KWIN_BUILD_TABBOX) include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set( kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} tabbox/tabbox.cpp tabbox/clientmodel.cpp tabbox/desktopchain.cpp tabbox/desktopmodel.cpp tabbox/switcheritem.cpp tabbox/tabboxconfig.cpp tabbox/tabboxhandler.cpp tabbox/tabbox_logging.cpp tabbox/x11_filter.cpp ) endif() if(KWIN_BUILD_ACTIVITIES) set( kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} activities.cpp ) endif() if (HAVE_LINUX_VT_H) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} virtual_terminal.cpp ) endif() kconfig_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc) kconfig_add_kcfg_files(kwin_KDEINIT_SRCS colorcorrection/colorcorrect_settings.kcfgc) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml dbusinterface.h KWin::DBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.Compositing.xml dbusinterface.h KWin::CompositorDBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.ColorCorrect.xml colorcorrection/colorcorrectdbusinterface.h KWin::ColorCorrect::ColorCorrectDBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.OrientationSensor.xml orientation_sensor.h KWin::OrientationSensor) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface ) qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/kf5_org.freedesktop.ScreenSaver.xml screenlocker_interface ) qt5_add_dbus_interface( kwin_KDEINIT_SRCS org.kde.kappmenu.xml appmenu_interface ) ki18n_wrap_ui(kwin_KDEINIT_SRCS debug_console.ui shortcutdialog.ui ) ########### target link libraries ############### set(kwin_OWN_LIBS kwineffects kwin4_effect_builtins ) set(kwin_QT_LIBS Qt5::Concurrent Qt5::DBus Qt5::Quick Qt5::Sensors Qt5::Script ) set(kwin_KDE_LIBS KF5::ConfigCore KF5::CoreAddons KF5::ConfigWidgets KF5::GlobalAccel KF5::GlobalAccelPrivate KF5::I18n KF5::Notifications KF5::Package KF5::Plasma KF5::WindowSystem KF5::QuickAddons KDecoration2::KDecoration KDecoration2::KDecoration2Private PW::KScreenLocker ) set(kwin_XLIB_LIBS ${X11_X11_LIB} ${X11_ICE_LIB} ${X11_SM_LIB} ) set(kwin_XCB_LIBS XCB::XCB XCB::XFIXES XCB::DAMAGE XCB::COMPOSITE XCB::SHAPE XCB::SYNC XCB::RENDER XCB::RANDR XCB::KEYSYMS XCB::SHM XCB::GLX XCB::ICCCM ) set(kwin_WAYLAND_LIBS XKB::XKB KF5::WaylandClient KF5::WaylandServer Wayland::Cursor ${CMAKE_THREAD_LIBS_INIT} ) if(KWIN_BUILD_ACTIVITIES) set(kwin_KDE_LIBS ${kwin_KDE_LIBS} KF5::Activities) endif() set(kwinLibs ${kwin_OWN_LIBS} ${kwin_QT_LIBS} ${kwin_KDE_LIBS} ${kwin_XLIB_LIBS} ${kwin_XCB_LIBS} ${kwin_WAYLAND_LIBS} ${UDEV_LIBS} Libinput::Libinput ) add_library(kwin SHARED ${kwin_KDEINIT_SRCS}) set_target_properties(kwin PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) target_link_libraries(kwin ${kwinLibs}) generate_export_header(kwin EXPORT_FILE_NAME kwin_export.h) target_link_libraries(kwin kwinglutils ${epoxy_LIBRARY}) kf5_add_kdeinit_executable(kwin_x11 main_x11.cpp) target_link_libraries(kdeinit_kwin_x11 kwin KF5::Crash Qt5::X11Extras) install(TARGETS kwin ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP ) install(TARGETS kdeinit_kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(TARGETS kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) set(kwin_XWAYLAND_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/xwl/xwayland.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/databridge.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/selection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/selection_source.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/transfer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/clipboard.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/dnd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/drag.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/drag_wl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/xwl/drag_x.cpp ) include(ECMQtDeclareLoggingCategory) ecm_qt_declare_logging_category(kwin_XWAYLAND_SRCS HEADER xwayland_logging.h IDENTIFIER KWIN_XWL CATEGORY_NAME kwin_xwl DEFAULT_SEVERITY Critical ) set(kwin_WAYLAND_SRCS tabletmodemanager.cpp main_wayland.cpp ) add_executable(kwin_wayland ${kwin_WAYLAND_SRCS} ${kwin_XWAYLAND_SRCS}) target_link_libraries(kwin_wayland kwin KF5::Crash) if (HAVE_LIBCAP) target_link_libraries(kwin_wayland ${Libcap_LIBRARIES}) endif() install(TARGETS kwin_wayland ${INSTALL_TARGETS_DEFAULT_ARGS} ) if (HAVE_LIBCAP) install( CODE "execute_process( COMMAND ${SETCAP_EXECUTABLE} CAP_SYS_NICE=+ep \$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/kwin_wayland)" ) endif() add_subdirectory(platformsupport) add_subdirectory(plugins) ########### install files ############### install( FILES kwin.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}.kcfg ) install( FILES colorcorrection/colorcorrect_settings.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}_colorcorrect.kcfg ) install( FILES kwin.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR} RENAME ${KWIN_NAME}.notifyrc) install( FILES org.kde.KWin.xml org.kde.kwin.Compositing.xml org.kde.kwin.ColorCorrect.xml org.kde.kwin.Effects.xml org.kde.KWin.VirtualDesktopManager.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kwin_export.h DESTINATION ${INCLUDE_INSTALL_DIR} COMPONENT Devel) # Install the KWin/Script service type install( FILES scripting/kwinscript.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) add_subdirectory(qml) if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() if (KF5DocTools_FOUND) add_subdirectory(doc) endif() add_subdirectory(kconf_update) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) include(CMakePackageConfigHelpers) set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KWinDBusInterface") configure_package_config_file(KWinDBusInterfaceConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/KWinDBusInterfaceConfig.cmake" PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KWinDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) diff --git a/abstract_output.cpp b/abstract_output.cpp index eaba345c7..1536697c9 100644 --- a/abstract_output.cpp +++ b/abstract_output.cpp @@ -1,262 +1,39 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2018 Roman Gilg 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 "abstract_output.h" -#include "wayland_server.h" -// KWayland -#include -#include -#include // KF5 #include #include namespace KWin { AbstractOutput::AbstractOutput(QObject *parent) : QObject(parent) { } AbstractOutput::~AbstractOutput() { - delete m_waylandOutputDevice.data(); - delete m_xdgOutput.data(); - delete m_waylandOutput.data(); -} - -QString AbstractOutput::name() const -{ - if (!m_waylandOutput) { - return i18n("unknown"); - } - return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); -} - -QRect AbstractOutput::geometry() const -{ - return QRect(m_globalPos, pixelSize() / scale()); -} - -QSize AbstractOutput::physicalSize() const -{ - return orientateSize(m_physicalSize); -} - -int AbstractOutput::refreshRate() const -{ - if (!m_waylandOutput) { - return 60000; - } - return m_waylandOutput->refreshRate(); -} - -void AbstractOutput::setGlobalPos(const QPoint &pos) -{ - m_globalPos = pos; - if (m_waylandOutput) { - m_waylandOutput->setGlobalPosition(pos); - } - if (m_waylandOutputDevice) { - m_waylandOutputDevice->setGlobalPosition(pos); - } - if (m_xdgOutput) { - m_xdgOutput->setLogicalPosition(pos); - m_xdgOutput->done(); - } -} - -void AbstractOutput::setScale(qreal scale) -{ - m_scale = scale; - if (m_waylandOutput) { - // this is the scale that clients will ideally use for their buffers - // this has to be an int which is fine - - // I don't know whether we want to round or ceil - // or maybe even set this to 3 when we're scaling to 1.5 - // don't treat this like it's chosen deliberately - m_waylandOutput->setScale(std::ceil(scale)); - } - if (m_waylandOutputDevice) { - m_waylandOutputDevice->setScaleF(scale); - } - if (m_xdgOutput) { - m_xdgOutput->setLogicalSize(pixelSize() / m_scale); - m_xdgOutput->done(); - } - emit modeChanged(); -} - -void AbstractOutput::setChanges(KWayland::Server::OutputChangeSet *changes) -{ - qCDebug(KWIN_CORE) << "Set changes in AbstractOutput."; - Q_ASSERT(!m_waylandOutputDevice.isNull()); - - if (!changes) { - qCDebug(KWIN_CORE) << "No changes."; - // No changes to an output is an entirely valid thing - } - //enabledChanged is handled by plugin code - if (changes->modeChanged()) { - qCDebug(KWIN_CORE) << "Setting new mode:" << changes->mode(); - m_waylandOutputDevice->setCurrentMode(changes->mode()); - updateMode(changes->mode()); - } - if (changes->transformChanged()) { - qCDebug(KWIN_CORE) << "Server setting transform: " << (int)(changes->transform()); - transform(changes->transform()); - } - if (changes->positionChanged()) { - qCDebug(KWIN_CORE) << "Server setting position: " << changes->position(); - setGlobalPos(changes->position()); - // may just work already! - } - if (changes->scaleChanged()) { - qCDebug(KWIN_CORE) << "Setting scale:" << changes->scale(); - setScale(changes->scaleF()); - } -} - -void AbstractOutput::setEnabled(bool enable) -{ - if (enable == isEnabled()) { - return; - } - if (enable) { - updateDpms(KWayland::Server::OutputInterface::DpmsMode::On); - initWaylandOutput(); - } else { - updateDpms(KWayland::Server::OutputInterface::DpmsMode::Off); - delete waylandOutput().data(); - } - waylandOutputDevice()->setEnabled(enable ? KWayland::Server::OutputDeviceInterface::Enablement::Enabled : - KWayland::Server::OutputDeviceInterface::Enablement::Disabled); -} - -void AbstractOutput::setWaylandMode(const QSize &size, int refreshRate) -{ - if (m_waylandOutput.isNull()) { - return; - } - m_waylandOutput->setCurrentMode(size, refreshRate); - if (m_xdgOutput) { - m_xdgOutput->setLogicalSize(pixelSize() / scale()); - m_xdgOutput->done(); - } -} - -void AbstractOutput::createXdgOutput() -{ - if (!m_waylandOutput || m_xdgOutput) { - return; - } - m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput); - m_xdgOutput->setLogicalSize(pixelSize() / scale()); - m_xdgOutput->setLogicalPosition(m_globalPos); - m_xdgOutput->done(); -} - -void AbstractOutput::initWaylandOutput() -{ - Q_ASSERT(m_waylandOutputDevice); - - if (!m_waylandOutput.isNull()) { - delete m_waylandOutput.data(); - m_waylandOutput.clear(); - } - m_waylandOutput = waylandServer()->display()->createOutput(); - createXdgOutput(); - - /* - * add base wayland output data - */ - m_waylandOutput->setManufacturer(m_waylandOutputDevice->manufacturer()); - m_waylandOutput->setModel(m_waylandOutputDevice->model()); - m_waylandOutput->setPhysicalSize(rawPhysicalSize()); - - /* - * add modes - */ - for(const auto &mode: m_waylandOutputDevice->modes()) { - KWayland::Server::OutputInterface::ModeFlags flags; - if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Current) { - flags |= KWayland::Server::OutputInterface::ModeFlag::Current; - } - if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Preferred) { - flags |= KWayland::Server::OutputInterface::ModeFlag::Preferred; - } - m_waylandOutput->addMode(mode.size, flags, mode.refreshRate); - } - m_waylandOutput->create(); - - /* - * set dpms - */ - m_waylandOutput->setDpmsSupported(m_supportsDpms); - // set to last known mode - m_waylandOutput->setDpmsMode(m_dpms); - connect(m_waylandOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this, - [this] (KWayland::Server::OutputInterface::DpmsMode mode) { - updateDpms(mode); - }, Qt::QueuedConnection - ); -} - -void AbstractOutput::initWaylandOutputDevice(const QString &model, - const QString &manufacturer, - const QByteArray &uuid, - const QVector &modes) -{ - if (!m_waylandOutputDevice.isNull()) { - delete m_waylandOutputDevice.data(); - m_waylandOutputDevice.clear(); - } - m_waylandOutputDevice = waylandServer()->display()->createOutputDevice(); - m_waylandOutputDevice->setUuid(uuid); - - if (!manufacturer.isEmpty()) { - m_waylandOutputDevice->setManufacturer(manufacturer); - } else { - m_waylandOutputDevice->setManufacturer(i18n("unknown")); - } - - m_waylandOutputDevice->setModel(model); - m_waylandOutputDevice->setPhysicalSize(m_physicalSize); - - int i = 0; - for (auto mode : modes) { - qCDebug(KWIN_CORE).nospace() << "Adding mode " << ++i << ": " << mode.size << " [" << mode.refreshRate << "]"; - m_waylandOutputDevice->addMode(mode); - } - m_waylandOutputDevice->create(); -} - -QSize AbstractOutput::orientateSize(const QSize &size) const -{ - if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) { - return size.transposed(); - } - return size; } } diff --git a/abstract_output.h b/abstract_output.h index f6bd238d7..63ae4ba1d 100644 --- a/abstract_output.h +++ b/abstract_output.h @@ -1,190 +1,78 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. -Copyright 2018 Roman Gilg +Copyright 2019 Roman Gilg 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_OUTPUT_H -#define KWIN_OUTPUT_H +#ifndef KWIN_ABSTRACT_OUTPUT_H +#define KWIN_ABSTRACT_OUTPUT_H -#include #include #include -#include -#include #include #include -#include - -#include -#include - -namespace KWayland -{ -namespace Server -{ -class OutputInterface; -class OutputDeviceInterface; -class OutputChangeSet; -class OutputManagementInterface; -class XdgOutputInterface; -} -} namespace KWin { namespace ColorCorrect { struct GammaRamp; } /** * Generic output representation in a Wayland session **/ class KWIN_EXPORT AbstractOutput : public QObject { Q_OBJECT public: explicit AbstractOutput(QObject *parent = nullptr); virtual ~AbstractOutput(); - QString name() const; - bool isEnabled() const { - return !m_waylandOutput.isNull(); - } - - virtual QSize pixelSize() const = 0; - qreal scale() const { - return m_scale; - } - /** - * The geometry of this output in global compositor co-ordinates (i.e scaled) - **/ - QRect geometry() const; - QSize physicalSize() const; - Qt::ScreenOrientation orientation() const { - return m_orientation; - } + virtual QString name() const = 0; + virtual QRect geometry() const = 0; /** * Current refresh rate in 1/ms. **/ - int refreshRate() const; + virtual int refreshRate() const = 0; - bool isInternal() const { - return m_internal; + virtual bool isInternal() const { + return false; } - - void setGlobalPos(const QPoint &pos); - void setScale(qreal scale); - - /** - * This sets the changes and tests them against the specific output. - **/ - void setChanges(KWayland::Server::OutputChangeSet *changeset); - - QPointer waylandOutput() const { - return m_waylandOutput; + virtual qreal scale() const { + return 1.; + } + virtual QSize physicalSize() const { + return QSize(); + } + virtual Qt::ScreenOrientation orientation() const { + return Qt::PrimaryOrientation; } - - /** - * Enable or disable the output. - * - * This differs from updateDpms as it also removes the wl_output. - * The default is on. - **/ - void setEnabled(bool enable); virtual int getGammaRampSize() const { return 0; } virtual bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) { Q_UNUSED(gamma); return false; } - -Q_SIGNALS: - void modeChanged(); - -protected: - void initWaylandOutput(); - void initWaylandOutputDevice(const QString &model, - const QString &manufacturer, - const QByteArray &uuid, - const QVector &modes); - - QPointer xdgOutput() const { - return m_xdgOutput; - } - void createXdgOutput(); - - QPointer waylandOutputDevice() const { - return m_waylandOutputDevice; - } - - QPoint globalPos() const { - return m_globalPos; - } - - QSize rawPhysicalSize() const { - return m_physicalSize; - } - void setRawPhysicalSize(const QSize &set) { - m_physicalSize = set; - } - - void setOrientation(Qt::ScreenOrientation set) { - m_orientation = set; - } - void setInternal(bool set) { - m_internal = set; - } - void setDpmsSupported(bool set) { - m_supportsDpms = set; - } - virtual void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) { - Q_UNUSED(mode); - } - virtual void updateMode(int modeIndex) { - Q_UNUSED(modeIndex); - } - virtual void transform(KWayland::Server::OutputDeviceInterface::Transform transform) { - Q_UNUSED(transform); - } - - void setWaylandMode(const QSize &size, int refreshRate); - - QSize orientateSize(const QSize &size) const; - -private: - QPointer m_waylandOutput; - QPointer m_xdgOutput; - QPointer m_waylandOutputDevice; - - KWayland::Server::OutputInterface::DpmsMode m_dpms = KWayland::Server::OutputInterface::DpmsMode::On; - - QPoint m_globalPos; - qreal m_scale = 1; - QSize m_physicalSize; - Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; - bool m_internal = false; - bool m_supportsDpms = false; }; } -#endif // KWIN_OUTPUT_H +#endif diff --git a/abstract_output.cpp b/abstract_wayland_output.cpp similarity index 87% copy from abstract_output.cpp copy to abstract_wayland_output.cpp index eaba345c7..c08a07304 100644 --- a/abstract_output.cpp +++ b/abstract_wayland_output.cpp @@ -1,262 +1,259 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. -Copyright 2018 Roman Gilg +Copyright 2019 Roman Gilg 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 "abstract_output.h" +#include "abstract_wayland_output.h" #include "wayland_server.h" // KWayland #include #include #include // KF5 #include #include namespace KWin { -AbstractOutput::AbstractOutput(QObject *parent) - : QObject(parent) +AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent) + : AbstractOutput(parent) { } -AbstractOutput::~AbstractOutput() +AbstractWaylandOutput::~AbstractWaylandOutput() { delete m_waylandOutputDevice.data(); delete m_xdgOutput.data(); delete m_waylandOutput.data(); } -QString AbstractOutput::name() const +QString AbstractWaylandOutput::name() const { if (!m_waylandOutput) { return i18n("unknown"); } return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); } -QRect AbstractOutput::geometry() const +QRect AbstractWaylandOutput::geometry() const { return QRect(m_globalPos, pixelSize() / scale()); } -QSize AbstractOutput::physicalSize() const +QSize AbstractWaylandOutput::physicalSize() const { return orientateSize(m_physicalSize); } -int AbstractOutput::refreshRate() const +int AbstractWaylandOutput::refreshRate() const { if (!m_waylandOutput) { return 60000; } return m_waylandOutput->refreshRate(); } -void AbstractOutput::setGlobalPos(const QPoint &pos) +void AbstractWaylandOutput::setGlobalPos(const QPoint &pos) { m_globalPos = pos; if (m_waylandOutput) { m_waylandOutput->setGlobalPosition(pos); } if (m_waylandOutputDevice) { m_waylandOutputDevice->setGlobalPosition(pos); } if (m_xdgOutput) { m_xdgOutput->setLogicalPosition(pos); m_xdgOutput->done(); } } -void AbstractOutput::setScale(qreal scale) +void AbstractWaylandOutput::setScale(qreal scale) { m_scale = scale; if (m_waylandOutput) { // this is the scale that clients will ideally use for their buffers // this has to be an int which is fine // I don't know whether we want to round or ceil // or maybe even set this to 3 when we're scaling to 1.5 // don't treat this like it's chosen deliberately m_waylandOutput->setScale(std::ceil(scale)); } if (m_waylandOutputDevice) { m_waylandOutputDevice->setScaleF(scale); } if (m_xdgOutput) { m_xdgOutput->setLogicalSize(pixelSize() / m_scale); m_xdgOutput->done(); } emit modeChanged(); } -void AbstractOutput::setChanges(KWayland::Server::OutputChangeSet *changes) +void AbstractWaylandOutput::setChanges(KWayland::Server::OutputChangeSet *changes) { - qCDebug(KWIN_CORE) << "Set changes in AbstractOutput."; + qCDebug(KWIN_CORE) << "Set changes in AbstractWaylandOutput."; Q_ASSERT(!m_waylandOutputDevice.isNull()); if (!changes) { qCDebug(KWIN_CORE) << "No changes."; // No changes to an output is an entirely valid thing } //enabledChanged is handled by plugin code if (changes->modeChanged()) { qCDebug(KWIN_CORE) << "Setting new mode:" << changes->mode(); m_waylandOutputDevice->setCurrentMode(changes->mode()); updateMode(changes->mode()); } if (changes->transformChanged()) { qCDebug(KWIN_CORE) << "Server setting transform: " << (int)(changes->transform()); transform(changes->transform()); } if (changes->positionChanged()) { qCDebug(KWIN_CORE) << "Server setting position: " << changes->position(); setGlobalPos(changes->position()); // may just work already! } if (changes->scaleChanged()) { qCDebug(KWIN_CORE) << "Setting scale:" << changes->scale(); setScale(changes->scaleF()); } } -void AbstractOutput::setEnabled(bool enable) +void AbstractWaylandOutput::setEnabled(bool enable) { if (enable == isEnabled()) { return; } if (enable) { updateDpms(KWayland::Server::OutputInterface::DpmsMode::On); initWaylandOutput(); } else { updateDpms(KWayland::Server::OutputInterface::DpmsMode::Off); delete waylandOutput().data(); } waylandOutputDevice()->setEnabled(enable ? KWayland::Server::OutputDeviceInterface::Enablement::Enabled : KWayland::Server::OutputDeviceInterface::Enablement::Disabled); } -void AbstractOutput::setWaylandMode(const QSize &size, int refreshRate) +void AbstractWaylandOutput::setWaylandMode(const QSize &size, int refreshRate) { if (m_waylandOutput.isNull()) { return; } m_waylandOutput->setCurrentMode(size, refreshRate); if (m_xdgOutput) { m_xdgOutput->setLogicalSize(pixelSize() / scale()); m_xdgOutput->done(); } } -void AbstractOutput::createXdgOutput() +void AbstractWaylandOutput::createXdgOutput() { if (!m_waylandOutput || m_xdgOutput) { return; } m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput); - m_xdgOutput->setLogicalSize(pixelSize() / scale()); - m_xdgOutput->setLogicalPosition(m_globalPos); - m_xdgOutput->done(); } -void AbstractOutput::initWaylandOutput() +void AbstractWaylandOutput::initWaylandOutput() { Q_ASSERT(m_waylandOutputDevice); if (!m_waylandOutput.isNull()) { delete m_waylandOutput.data(); m_waylandOutput.clear(); } m_waylandOutput = waylandServer()->display()->createOutput(); createXdgOutput(); /* * add base wayland output data */ m_waylandOutput->setManufacturer(m_waylandOutputDevice->manufacturer()); m_waylandOutput->setModel(m_waylandOutputDevice->model()); m_waylandOutput->setPhysicalSize(rawPhysicalSize()); /* * add modes */ for(const auto &mode: m_waylandOutputDevice->modes()) { KWayland::Server::OutputInterface::ModeFlags flags; if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Current) { flags |= KWayland::Server::OutputInterface::ModeFlag::Current; } if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Preferred) { flags |= KWayland::Server::OutputInterface::ModeFlag::Preferred; } m_waylandOutput->addMode(mode.size, flags, mode.refreshRate); } m_waylandOutput->create(); /* * set dpms */ m_waylandOutput->setDpmsSupported(m_supportsDpms); // set to last known mode m_waylandOutput->setDpmsMode(m_dpms); connect(m_waylandOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this, [this] (KWayland::Server::OutputInterface::DpmsMode mode) { updateDpms(mode); }, Qt::QueuedConnection ); } -void AbstractOutput::initWaylandOutputDevice(const QString &model, +void AbstractWaylandOutput::initWaylandOutputDevice(const QString &model, const QString &manufacturer, const QByteArray &uuid, const QVector &modes) { if (!m_waylandOutputDevice.isNull()) { delete m_waylandOutputDevice.data(); m_waylandOutputDevice.clear(); } m_waylandOutputDevice = waylandServer()->display()->createOutputDevice(); m_waylandOutputDevice->setUuid(uuid); if (!manufacturer.isEmpty()) { m_waylandOutputDevice->setManufacturer(manufacturer); } else { m_waylandOutputDevice->setManufacturer(i18n("unknown")); } m_waylandOutputDevice->setModel(model); m_waylandOutputDevice->setPhysicalSize(m_physicalSize); int i = 0; for (auto mode : modes) { qCDebug(KWIN_CORE).nospace() << "Adding mode " << ++i << ": " << mode.size << " [" << mode.refreshRate << "]"; m_waylandOutputDevice->addMode(mode); } m_waylandOutputDevice->create(); } -QSize AbstractOutput::orientateSize(const QSize &size) const +QSize AbstractWaylandOutput::orientateSize(const QSize &size) const { if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) { return size.transposed(); } return size; } } diff --git a/abstract_output.h b/abstract_wayland_output.h similarity index 86% copy from abstract_output.h copy to abstract_wayland_output.h index f6bd238d7..c11c3e7dc 100644 --- a/abstract_output.h +++ b/abstract_wayland_output.h @@ -1,190 +1,182 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. -Copyright 2018 Roman Gilg +Copyright 2019 Roman Gilg 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_OUTPUT_H -#define KWIN_OUTPUT_H +#ifndef KWIN_ABSTRACT_WAYLAND_OUTPUT_H +#define KWIN_ABSTRACT_WAYLAND_OUTPUT_H +#include "abstract_output.h" #include #include #include #include #include #include #include #include #include #include namespace KWayland { namespace Server { class OutputInterface; class OutputDeviceInterface; class OutputChangeSet; class OutputManagementInterface; class XdgOutputInterface; } } namespace KWin { -namespace ColorCorrect { -struct GammaRamp; -} - /** * Generic output representation in a Wayland session **/ -class KWIN_EXPORT AbstractOutput : public QObject +class KWIN_EXPORT AbstractWaylandOutput : public AbstractOutput { Q_OBJECT public: - explicit AbstractOutput(QObject *parent = nullptr); - virtual ~AbstractOutput(); + explicit AbstractWaylandOutput(QObject *parent = nullptr); + virtual ~AbstractWaylandOutput(); - QString name() const; + QString name() const override; bool isEnabled() const { return !m_waylandOutput.isNull(); } virtual QSize pixelSize() const = 0; - qreal scale() const { + qreal scale() const override { return m_scale; } /** * The geometry of this output in global compositor co-ordinates (i.e scaled) **/ - QRect geometry() const; - QSize physicalSize() const; - Qt::ScreenOrientation orientation() const { + QRect geometry() const override; + QSize physicalSize() const override; + Qt::ScreenOrientation orientation() const override { return m_orientation; } /** * Current refresh rate in 1/ms. **/ - int refreshRate() const; + int refreshRate() const override; - bool isInternal() const { + bool isInternal() const override { return m_internal; } void setGlobalPos(const QPoint &pos); void setScale(qreal scale); /** * This sets the changes and tests them against the specific output. **/ void setChanges(KWayland::Server::OutputChangeSet *changeset); QPointer waylandOutput() const { return m_waylandOutput; } /** * Enable or disable the output. * * This differs from updateDpms as it also removes the wl_output. * The default is on. **/ void setEnabled(bool enable); - virtual int getGammaRampSize() const { - return 0; - } - virtual bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) { - Q_UNUSED(gamma); - return false; - } - Q_SIGNALS: void modeChanged(); protected: void initWaylandOutput(); void initWaylandOutputDevice(const QString &model, const QString &manufacturer, const QByteArray &uuid, const QVector &modes); QPointer xdgOutput() const { return m_xdgOutput; } void createXdgOutput(); QPointer waylandOutputDevice() const { return m_waylandOutputDevice; } QPoint globalPos() const { return m_globalPos; } QSize rawPhysicalSize() const { return m_physicalSize; } void setRawPhysicalSize(const QSize &set) { m_physicalSize = set; } void setOrientation(Qt::ScreenOrientation set) { m_orientation = set; } + bool internal() const { + return m_internal; + } void setInternal(bool set) { m_internal = set; } void setDpmsSupported(bool set) { m_supportsDpms = set; } virtual void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) { Q_UNUSED(mode); } virtual void updateMode(int modeIndex) { Q_UNUSED(modeIndex); } virtual void transform(KWayland::Server::OutputDeviceInterface::Transform transform) { Q_UNUSED(transform); } void setWaylandMode(const QSize &size, int refreshRate); QSize orientateSize(const QSize &size) const; private: QPointer m_waylandOutput; QPointer m_xdgOutput; QPointer m_waylandOutputDevice; KWayland::Server::OutputInterface::DpmsMode m_dpms = KWayland::Server::OutputInterface::DpmsMode::On; QPoint m_globalPos; qreal m_scale = 1; QSize m_physicalSize; Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; bool m_internal = false; bool m_supportsDpms = false; }; } #endif // KWIN_OUTPUT_H diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 0aed9e5ed..d80a6f4cc 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,448 +1,406 @@ add_definitions(-DKWIN_UNIT_TEST) remove_definitions(-DQT_USE_QSTRINGBUILDER) add_subdirectory(libkwineffects) add_subdirectory(libxrenderutils) add_subdirectory(integration) add_subdirectory(libinput) if (HAVE_DRM) add_subdirectory(drm) endif() add_subdirectory(tabbox) ######################################################## # Test ScreenPaintData ######################################################## set( testScreenPaintData_SRCS test_screen_paint_data.cpp ) add_executable(testScreenPaintData ${testScreenPaintData_SRCS}) target_link_libraries( testScreenPaintData kwineffects Qt5::Test Qt5::Widgets KF5::WindowSystem) add_test(NAME kwin-testScreenPaintData COMMAND testScreenPaintData) ecm_mark_as_test(testScreenPaintData) ######################################################## # Test WindowPaintData ######################################################## set( testWindowPaintData_SRCS test_window_paint_data.cpp ) add_executable(testWindowPaintData ${testWindowPaintData_SRCS}) target_link_libraries( testWindowPaintData kwineffects Qt5::Widgets Qt5::Test ) add_test(NAME kwin-testWindowPaintData COMMAND testWindowPaintData) ecm_mark_as_test(testWindowPaintData) ######################################################## # Test VirtualDesktopManager ######################################################## set( testVirtualDesktops_SRCS test_virtual_desktops.cpp ../virtualdesktops.cpp ) add_executable(testVirtualDesktops ${testVirtualDesktops_SRCS}) target_link_libraries( testVirtualDesktops Qt5::Test Qt5::Widgets KF5::I18n KF5::GlobalAccel KF5::ConfigCore KF5::WindowSystem KF5::WaylandServer ) add_test(NAME kwin-testVirtualDesktops COMMAND testVirtualDesktops) ecm_mark_as_test(testVirtualDesktops) ######################################################## # Test ClientMachine ######################################################## set( testClientMachine_SRCS test_client_machine.cpp ../client_machine.cpp ) add_executable( testClientMachine ${testClientMachine_SRCS} ) set_target_properties(testClientMachine PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_link_libraries( testClientMachine Qt5::Concurrent Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::XFIXES ${X11_X11_LIB} # to make jenkins happy ) add_test(NAME kwin-testClientMachine COMMAND testClientMachine) ecm_mark_as_test(testClientMachine) ######################################################## # Test XcbWrapper ######################################################## set( testXcbWrapper_SRCS test_xcb_wrapper.cpp ) add_executable( testXcbWrapper ${testXcbWrapper_SRCS} ) target_link_libraries( testXcbWrapper Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(NAME kwin-testXcbWrapper COMMAND testXcbWrapper) ecm_mark_as_test(testXcbWrapper) if (XCB_ICCCM_FOUND) add_executable( testXcbSizeHints test_xcb_size_hints.cpp ) set_target_properties(testXcbSizeHints PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_link_libraries( testXcbSizeHints Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::ICCCM ) add_test(NAME kwin-testXcbSizeHints COMMAND testXcbSizeHints) ecm_mark_as_test(testXcbSizeHints) endif() ######################################################## # Test XcbWindow ######################################################## set( testXcbWindow_SRCS test_xcb_window.cpp ) add_executable( testXcbWindow ${testXcbWindow_SRCS} ) target_link_libraries( testXcbWindow Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(NAME kwin-testXcbWindow COMMAND testXcbWindow) ecm_mark_as_test(testXcbWindow) ######################################################## # Test BuiltInEffectLoader ######################################################## set( testBuiltInEffectLoader_SRCS test_builtin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testBuiltInEffectLoader ${testBuiltInEffectLoader_SRCS}) set_target_properties(testBuiltInEffectLoader PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_link_libraries(testBuiltInEffectLoader Qt5::Concurrent Qt5::Test Qt5::X11Extras KF5::Package kwineffects kwin4_effect_builtins ) add_test(NAME kwin-testBuiltInEffectLoader COMMAND testBuiltInEffectLoader) ecm_mark_as_test(testBuiltInEffectLoader) ######################################################## # Test ScriptedEffectLoader ######################################################## include_directories(${KWIN_SOURCE_DIR}) set( testScriptedEffectLoader_SRCS test_scripted_effectloader.cpp mock_abstract_client.cpp mock_effectshandler.cpp mock_screens.cpp mock_workspace.cpp ../effectloader.cpp ../scripting/scriptedeffect.cpp ../scripting/scriptingutils.cpp ../scripting/scripting_logging.cpp ../screens.cpp ../orientation_sensor.cpp ) kconfig_add_kcfg_files(testScriptedEffectLoader_SRCS ../settings.kcfgc) qt5_add_dbus_adaptor( testScriptedEffectLoader_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.kde.kwin.OrientationSensor.xml ${CMAKE_CURRENT_SOURCE_DIR}/../orientation_sensor.h KWin::OrientationSensor) add_executable( testScriptedEffectLoader ${testScriptedEffectLoader_SRCS}) target_link_libraries(testScriptedEffectLoader Qt5::Concurrent Qt5::Qml Qt5::Script Qt5::Sensors Qt5::Test Qt5::X11Extras KF5::ConfigGui KF5::GlobalAccel KF5::I18n KF5::Notifications KF5::Package kwineffects kwin4_effect_builtins ) add_test(NAME kwin-testScriptedEffectLoader COMMAND testScriptedEffectLoader) ecm_mark_as_test(testScriptedEffectLoader) ######################################################## # Test PluginEffectLoader ######################################################## set( testPluginEffectLoader_SRCS test_plugin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testPluginEffectLoader ${testPluginEffectLoader_SRCS}) target_link_libraries(testPluginEffectLoader Qt5::Concurrent Qt5::Test Qt5::X11Extras KF5::Package kwineffects kwin4_effect_builtins ) add_test(NAME kwin-testPluginEffectLoader COMMAND testPluginEffectLoader) ecm_mark_as_test(testPluginEffectLoader) ######################################################## # FakeEffectPlugin ######################################################## add_library(fakeeffectplugin MODULE fakeeffectplugin.cpp) set_target_properties(fakeeffectplugin PROPERTIES PREFIX "") target_link_libraries(fakeeffectplugin kwineffects) ######################################################## # FakeEffectPlugin-Version ######################################################## add_library(effectversionplugin MODULE fakeeffectplugin_version.cpp) set_target_properties(effectversionplugin PROPERTIES PREFIX "") target_link_libraries(effectversionplugin kwineffects) ######################################################## # Test Screens ######################################################## set( testScreens_SRCS test_screens.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../screens.cpp ../x11eventfilter.cpp ../orientation_sensor.cpp ) kconfig_add_kcfg_files(testScreens_SRCS ../settings.kcfgc) qt5_add_dbus_adaptor( testScreens_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.kde.kwin.OrientationSensor.xml ${CMAKE_CURRENT_SOURCE_DIR}/../orientation_sensor.h KWin::OrientationSensor) add_executable( testScreens ${testScreens_SRCS}) target_include_directories(testScreens BEFORE PRIVATE ./) target_link_libraries(testScreens Qt5::DBus Qt5::Sensors Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::Notifications KF5::WindowSystem ) add_test(NAME kwin_testScreens COMMAND testScreens) ecm_mark_as_test(testScreens) -######################################################## -# Test XrandRScreens -######################################################## -set( testXRandRScreens_SRCS - test_xrandr_screens.cpp - mock_abstract_client.cpp - mock_client.cpp - mock_screens.cpp - mock_workspace.cpp - ../screens.cpp - ../plugins/platforms/x11/standalone/screens_xrandr.cpp - ../xcbutils.cpp # init of extensions - ../x11eventfilter.cpp - ../orientation_sensor.cpp -) -kconfig_add_kcfg_files(testXRandRScreens_SRCS ../settings.kcfgc) -qt5_add_dbus_adaptor( testXRandRScreens_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.kde.kwin.OrientationSensor.xml ${CMAKE_CURRENT_SOURCE_DIR}/../orientation_sensor.h KWin::OrientationSensor) -add_executable( testXRandRScreens ${testXRandRScreens_SRCS} ) -target_link_libraries( testXRandRScreens - Qt5::Test - Qt5::DBus - Qt5::Gui - Qt5::Sensors - Qt5::Widgets - KF5::ConfigCore - KF5::ConfigGui - KF5::I18n - KF5::Notifications - KF5::WindowSystem - XCB::XCB - XCB::RANDR - XCB::XFIXES - XCB::SYNC - XCB::COMPOSITE - XCB::DAMAGE - XCB::GLX - XCB::SHM -) - -add_test(NAME kwin-testXRandRScreens COMMAND testXRandRScreens) -ecm_mark_as_test(testXRandRScreens) - ######################################################## # Test ScreenEdges ######################################################## set( testScreenEdges_SRCS test_screen_edges.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../atoms.cpp ../gestures.cpp ../screens.cpp ../screenedge.cpp ../virtualdesktops.cpp ../xcbutils.cpp # init of extensions ../plugins/platforms/x11/standalone/edge.cpp ../orientation_sensor.cpp ) kconfig_add_kcfg_files(testScreenEdges_SRCS ../settings.kcfgc) qt5_add_dbus_interface( testScreenEdges_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/kf5_org.freedesktop.ScreenSaver.xml screenlocker_interface ) qt5_add_dbus_adaptor( testScreenEdges_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.kde.kwin.OrientationSensor.xml ${CMAKE_CURRENT_SOURCE_DIR}/../orientation_sensor.h KWin::OrientationSensor) add_executable( testScreenEdges ${testScreenEdges_SRCS}) set_target_properties(testScreenEdges PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW") target_include_directories(testScreenEdges BEFORE PRIVATE ./) target_link_libraries(testScreenEdges Qt5::DBus Qt5::Sensors Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::GlobalAccel KF5::Notifications KF5::WindowSystem KF5::WaylandServer XCB::XCB XCB::RANDR XCB::XFIXES XCB::SYNC XCB::COMPOSITE XCB::DAMAGE XCB::GLX XCB::SHM ) add_test(NAME kwin_testScreenEdges COMMAND testScreenEdges) ecm_mark_as_test(testScreenEdges) ######################################################## # Test OnScreenNotification ######################################################## set( testOnScreenNotification_SRCS onscreennotificationtest.cpp ../onscreennotification.cpp ../input_event_spy.cpp ) add_executable( testOnScreenNotification ${testOnScreenNotification_SRCS}) target_link_libraries(testOnScreenNotification Qt5::Test Qt5::Widgets # QAction include Qt5::Quick Qt5::DBus KF5::ConfigCore ) add_test(NAME kwin-testOnScreenNotification COMMAND testOnScreenNotification) ecm_mark_as_test(testOnScreenNotification) ######################################################## # Test Gestures ######################################################## set( testGestures_SRCS test_gestures.cpp ../gestures.cpp ) add_executable( testGestures ${testGestures_SRCS}) target_link_libraries(testGestures Qt5::Test ) add_test(NAME kwin-testGestures COMMAND testGestures) ecm_mark_as_test(testGestures) ######################################################## # Test X11 TimestampUpdate ######################################################## add_executable(testX11TimestampUpdate test_x11_timestamp_update.cpp) target_link_libraries(testX11TimestampUpdate Qt5::Test KF5::CoreAddons kwin ) add_test(NAME kwin-testX11TimestampUpdate COMMAND testX11TimestampUpdate) ecm_mark_as_test(testX11TimestampUpdate) set(testOpenGLContextAttributeBuilder_SRCS opengl_context_attribute_builder_test.cpp ../abstract_opengl_context_attribute_builder.cpp ../egl_context_attribute_builder.cpp ) if(HAVE_EPOXY_GLX) set(testOpenGLContextAttributeBuilder_SRCS ${testOpenGLContextAttributeBuilder_SRCS} ../plugins/platforms/x11/standalone/glx_context_attribute_builder.cpp) endif() add_executable(testOpenGLContextAttributeBuilder ${testOpenGLContextAttributeBuilder_SRCS}) target_link_libraries(testOpenGLContextAttributeBuilder Qt5::Test) add_test(NAME kwin-testOpenGLContextAttributeBuilder COMMAND testOpenGLContextAttributeBuilder) ecm_mark_as_test(testOpenGLContextAttributeBuilder) set(testXkb_SRCS test_xkb.cpp ../xkb.cpp ) add_executable(testXkb ${testXkb_SRCS}) target_link_libraries(testXkb Qt5::Test Qt5::Gui Qt5::Widgets KF5::ConfigCore KF5::WindowSystem KF5::WaylandServer XKB::XKB ) add_test(NAME kwin-testXkb COMMAND testXkb) ecm_mark_as_test(testXkb) if(HAVE_GBM) add_executable(testGbmSurface test_gbm_surface.cpp ../plugins/platforms/drm/gbm_surface.cpp) target_link_libraries(testGbmSurface Qt5::Test) add_test(NAME kwin-testGbmSurface COMMAND testGbmSurface) ecm_mark_as_test(testGbmSurface) endif() add_executable(testVirtualKeyboardDBus test_virtualkeyboard_dbus.cpp ../virtualkeyboard_dbus.cpp) target_link_libraries(testVirtualKeyboardDBus Qt5::Test Qt5::DBus ) add_test(NAME kwin-testVirtualKeyboardDBus COMMAND testVirtualKeyboardDBus) ecm_mark_as_test(testVirtualKeyboardDBus) diff --git a/autotests/test_xrandr_screens.cpp b/autotests/test_xrandr_screens.cpp deleted file mode 100644 index e0f6b14a5..000000000 --- a/autotests/test_xrandr_screens.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/******************************************************************** -KWin - the KDE window manager -This file is part of the KDE project. - -Copyright (C) 2014 Martin Gräßlin - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ -#include "../plugins/platforms/x11/standalone/screens_xrandr.h" -#include "../cursor.h" -#include "../xcbutils.h" -#include "mock_workspace.h" -// Qt -#include -// system -#include - -Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core") - -// mocking -namespace KWin -{ - -QPoint Cursor::pos() -{ - return QPoint(0, 0); -} -} // namespace KWin - -static xcb_window_t s_rootWindow = XCB_WINDOW_NONE; -static xcb_connection_t *s_connection = nullptr; - -using namespace KWin; -using namespace KWin::Xcb; - -class TestXRandRScreens : public QObject -{ - Q_OBJECT -private Q_SLOTS: - void initTestCase(); - void cleanupTestCase(); - void testStartup(); - void testChange(); - void testMultipleChanges(); -private: - QScopedPointer m_xserver; -}; - -void TestXRandRScreens::initTestCase() -{ - // TODO: turn into init instead of initTestCase - // needs to be initTestCase as KWin::connection caches the first created xcb_connection_t - // thus changing X server for each test run would create problems - qsrand(QDateTime::currentMSecsSinceEpoch()); - // first reset just to be sure - s_connection = nullptr; - s_rootWindow = XCB_WINDOW_NONE; - // start X Server - m_xserver.reset(new QProcess); - // use pipe to pass fd to Xephyr to get back the display id - int pipeFds[2]; - QVERIFY(pipe(pipeFds) == 0); - // using Xephyr as Xvfb doesn't support render extension - m_xserver->start(QStringLiteral("Xephyr"), QStringList({ QStringLiteral("-displayfd"), QString::number(pipeFds[1]) })); - QVERIFY(m_xserver->waitForStarted()); - QCOMPARE(m_xserver->state(), QProcess::Running); - - // reads from pipe, closes write side - close(pipeFds[1]); - - QFile readPipe; - QVERIFY(readPipe.open(pipeFds[0], QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)); - QByteArray displayNumber = readPipe.readLine(); - readPipe.close(); - - displayNumber.prepend(QByteArray(":")); - displayNumber.remove(displayNumber.size() -1, 1); - - // create X connection - int screen = 0; - s_connection = xcb_connect(displayNumber.constData(), &screen); - QVERIFY(s_connection); - - // set root window - xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(s_connection)); - for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(s_connection)); - it.rem; - --screen, xcb_screen_next(&it)) { - if (screen == 0) { - s_rootWindow = iter.data->root; - break; - } - } - QVERIFY(s_rootWindow != XCB_WINDOW_NONE); - qApp->setProperty("x11RootWindow", QVariant::fromValue(s_rootWindow)); - qApp->setProperty("x11Connection", QVariant::fromValue(s_connection)); - - // get the extensions - if (!Extensions::self()->isRandrAvailable()) { - QSKIP("XRandR extension required"); - } - for (const auto &extension : Extensions::self()->extensions()) { - if (extension.name == QByteArrayLiteral("RANDR")) { - if (extension.version < 1 * 0x10 + 4) { - QSKIP("At least XRandR 1.4 required"); - } - } - } -} - -void TestXRandRScreens::cleanupTestCase() -{ - Extensions::destroy(); - // close connection - xcb_disconnect(s_connection); - s_connection = nullptr; - s_rootWindow = XCB_WINDOW_NONE; - // kill X - m_xserver->terminate(); - m_xserver->waitForFinished(); -} - -void TestXRandRScreens::testStartup() -{ - KWin::MockWorkspace ws; - QScopedPointer screens(new XRandRScreens(this)); - QVERIFY(!screens->eventTypes().isEmpty()); - QCOMPARE(screens->eventTypes().first(), Xcb::Extensions::self()->randrNotifyEvent()); - QCOMPARE(screens->extension(), 0); - QCOMPARE(screens->genericEventTypes(), QVector{0}); - screens->init(); - QRect xephyrDefault = QRect(0, 0, 640, 480); - QCOMPARE(screens->count(), 1); - QCOMPARE(screens->geometry(0), xephyrDefault); - QCOMPARE(screens->geometry(1), QRect()); - QCOMPARE(screens->geometry(-1), QRect()); - QCOMPARE(static_cast(screens.data())->geometry(), xephyrDefault); - QCOMPARE(screens->size(0), xephyrDefault.size()); - QCOMPARE(screens->size(1), QSize()); - QCOMPARE(screens->size(-1), QSize()); - QCOMPARE(static_cast(screens.data())->size(), xephyrDefault.size()); - // unfortunately we only have one output, so let's try at least to test somewhat - QCOMPARE(screens->number(QPoint(0, 0)), 0); - QCOMPARE(screens->number(QPoint(639, 479)), 0); - QCOMPARE(screens->number(QPoint(1280, 1024)), 0); - - // let's change the mode - RandR::CurrentResources resources(s_rootWindow); - auto *crtcs = resources.crtcs(); - auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data()); - auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data()); - RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[0].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); - QVERIFY(!setter.isNull()); - - // now let's recreate the XRandRScreens - screens.reset(new XRandRScreens(this)); - screens->init(); - QRect geo = QRect(0, 0, modes[0].width, modes[0].height); - QCOMPARE(screens->count(), 1); - QCOMPARE(screens->geometry(0), geo); - QCOMPARE(static_cast(screens.data())->geometry(), geo); - QCOMPARE(screens->size(0), geo.size()); - QCOMPARE(static_cast(screens.data())->size(), geo.size()); -} - -void TestXRandRScreens::testChange() -{ - KWin::MockWorkspace ws; - QScopedPointer screens(new XRandRScreens(this)); - screens->init(); - - // create some signal spys - QSignalSpy changedSpy(screens.data(), SIGNAL(changed())); - QVERIFY(changedSpy.isValid()); - QVERIFY(changedSpy.isEmpty()); - QVERIFY(changedSpy.wait()); - changedSpy.clear(); - QSignalSpy geometrySpy(screens.data(), SIGNAL(geometryChanged())); - QVERIFY(geometrySpy.isValid()); - QVERIFY(geometrySpy.isEmpty()); - QSignalSpy sizeSpy(screens.data(), SIGNAL(sizeChanged())); - QVERIFY(sizeSpy.isValid()); - QVERIFY(sizeSpy.isEmpty()); - - // clear the event loop - while (xcb_generic_event_t *e = xcb_poll_for_event(s_connection)) { - free(e); - } - - // let's change - RandR::CurrentResources resources(s_rootWindow); - auto *crtcs = resources.crtcs(); - auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data()); - auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data()); - RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[1].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); - xcb_flush(s_connection); - QVERIFY(!setter.isNull()); - QVERIFY(setter->status == XCB_RANDR_SET_CONFIG_SUCCESS); - - xcb_generic_event_t *e = xcb_wait_for_event(s_connection); - screens->event(e); - free(e); - - QVERIFY(changedSpy.wait()); - QCOMPARE(changedSpy.size(), 1); - QCOMPARE(sizeSpy.size(), 1); - QCOMPARE(geometrySpy.size(), 1); - QRect geo = QRect(0, 0, modes[1].width, modes[1].height); - QCOMPARE(screens->count(), 1); - QCOMPARE(screens->geometry(0), geo); - QCOMPARE(static_cast(screens.data())->geometry(), geo); - QCOMPARE(screens->size(0), geo.size()); - QCOMPARE(static_cast(screens.data())->size(), geo.size()); -} - -void TestXRandRScreens::testMultipleChanges() -{ - KWin::MockWorkspace ws; - // multiple changes should only hit one changed signal - QScopedPointer screens(new XRandRScreens(this)); - screens->init(); - - // create some signal spys - QSignalSpy changedSpy(screens.data(), SIGNAL(changed())); - QVERIFY(changedSpy.isValid()); - QVERIFY(changedSpy.isEmpty()); - QVERIFY(changedSpy.wait()); - changedSpy.clear(); - QSignalSpy geometrySpy(screens.data(), SIGNAL(geometryChanged())); - QVERIFY(geometrySpy.isValid()); - QVERIFY(geometrySpy.isEmpty()); - QSignalSpy sizeSpy(screens.data(), SIGNAL(sizeChanged())); - QVERIFY(sizeSpy.isValid()); - QVERIFY(sizeSpy.isEmpty()); - - // clear the event loop - while (xcb_generic_event_t *e = xcb_poll_for_event(s_connection)) { - free(e); - } - - // first change - RandR::CurrentResources resources(s_rootWindow); - auto *crtcs = resources.crtcs(); - auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data()); - auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data()); - RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[0].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); - QVERIFY(!setter.isNull()); - QVERIFY(setter->status == XCB_RANDR_SET_CONFIG_SUCCESS); - // second change - RandR::SetCrtcConfig setter2(crtcs[0], setter->timestamp, resources->config_timestamp, 0, 0, modes[1].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs); - QVERIFY(!setter2.isNull()); - QVERIFY(setter2->status == XCB_RANDR_SET_CONFIG_SUCCESS); - - auto passEvent = [&screens]() { - xcb_generic_event_t *e = xcb_wait_for_event(s_connection); - screens->event(e); - free(e); - }; - passEvent(); - passEvent(); - - QVERIFY(changedSpy.wait()); - QCOMPARE(changedSpy.size(), 1); - // previous state was modes[1] so the size didn't change - QVERIFY(sizeSpy.isEmpty()); - QVERIFY(geometrySpy.isEmpty()); - QRect geo = QRect(0, 0, modes[1].width, modes[1].height); - QCOMPARE(screens->count(), 1); - QCOMPARE(screens->geometry(0), geo); - QCOMPARE(static_cast(screens.data())->geometry(), geo); - QCOMPARE(screens->size(0), geo.size()); - QCOMPARE(static_cast(screens.data())->size(), geo.size()); -} - -QTEST_GUILESS_MAIN(TestXRandRScreens) -#include "test_xrandr_screens.moc" diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp index 371029562..1bce5d3a5 100644 --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -1,1214 +1,1214 @@ /******************************************************************** 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 "drm_output.h" #include "drm_backend.h" #include "drm_object_plane.h" #include "drm_object_crtc.h" #include "drm_object_connector.h" #include #include "composite.h" #include "logind.h" #include "logging.h" #include "main.h" #include "orientation_sensor.h" #include "screens_drm.h" #include "wayland_server.h" // KWayland #include // KF5 #include #include #include // Qt #include #include #include // drm #include #include #include namespace KWin { DrmOutput::DrmOutput(DrmBackend *backend) - : AbstractOutput(backend) + : AbstractWaylandOutput(backend) , m_backend(backend) { } DrmOutput::~DrmOutput() { Q_ASSERT(!m_pageFlipPending); if (!m_deleted) { teardown(); } } void DrmOutput::teardown() { m_deleted = true; hideCursor(); m_crtc->blank(); if (m_primaryPlane) { // TODO: when having multiple planes, also clean up these m_primaryPlane->setOutput(nullptr); if (m_backend->deleteBufferAfterPageFlip()) { delete m_primaryPlane->current(); } m_primaryPlane->setCurrent(nullptr); } m_crtc->setOutput(nullptr); m_conn->setOutput(nullptr); delete m_cursor[0]; delete m_cursor[1]; if (!m_pageFlipPending) { deleteLater(); } //else will be deleted in the page flip handler //this is needed so that the pageflipcallback handle isn't deleted } void DrmOutput::releaseGbm() { if (DrmBuffer *b = m_crtc->current()) { b->releaseGbm(); } if (m_primaryPlane && m_primaryPlane->current()) { m_primaryPlane->current()->releaseGbm(); } } bool DrmOutput::hideCursor() { return drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0) == 0; } bool DrmOutput::showCursor(DrmDumbBuffer *c) { const QSize &s = c->size(); return drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0; } bool DrmOutput::showCursor() { const bool ret = showCursor(m_cursor[m_cursorIndex]); if (!ret) { return ret; } if (m_hasNewCursor) { m_cursorIndex = (m_cursorIndex + 1) % 2; m_hasNewCursor = false; } return ret; } int orientationToRotation(Qt::ScreenOrientation orientation) { switch (orientation) { case Qt::PrimaryOrientation: case Qt::LandscapeOrientation: return 0; case Qt::InvertedPortraitOrientation: return 90; case Qt::InvertedLandscapeOrientation: return 180; case Qt::PortraitOrientation: return 270; } Q_UNREACHABLE(); return 0; } QMatrix4x4 DrmOutput::matrixDisplay(const QSize &s) const { QMatrix4x4 matrix; const int angle = orientationToRotation(orientation()); if (angle) { const QSize center = s / 2; matrix.translate(center.width(), center.height()); matrix.rotate(angle, 0, 0, 1); matrix.translate(-center.width(), -center.height()); } matrix.scale(scale()); return matrix; } void DrmOutput::updateCursor() { QImage cursorImage = m_backend->softwareCursor(); if (cursorImage.isNull()) { return; } m_hasNewCursor = true; QImage *c = m_cursor[m_cursorIndex]->image(); c->fill(Qt::transparent); QPainter p; p.begin(c); p.setWorldTransform(matrixDisplay(QSize(cursorImage.width(), cursorImage.height())).toTransform()); p.drawImage(QPoint(0, 0), cursorImage); p.end(); } void DrmOutput::moveCursor(const QPoint &globalPos) { const QMatrix4x4 hotspotMatrix = matrixDisplay(m_backend->softwareCursor().size()); - QPoint p = globalPos-AbstractOutput::globalPos(); + QPoint p = globalPos - AbstractWaylandOutput::globalPos(); switch (orientation()) { case Qt::PrimaryOrientation: case Qt::LandscapeOrientation: break; case Qt::PortraitOrientation: p = QPoint(p.y(), pixelSize().height() - p.x()); break; case Qt::InvertedPortraitOrientation: p = QPoint(pixelSize().width() - p.y(), p.x()); break; case Qt::InvertedLandscapeOrientation: p = QPoint(pixelSize().width() - p.x(), pixelSize().height() - p.y()); break; } p *= scale(); p -= hotspotMatrix.map(m_backend->softwareCursorHotspot()); drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); } static QHash s_connectorNames = { {DRM_MODE_CONNECTOR_Unknown, QByteArrayLiteral("Unknown")}, {DRM_MODE_CONNECTOR_VGA, QByteArrayLiteral("VGA")}, {DRM_MODE_CONNECTOR_DVII, QByteArrayLiteral("DVI-I")}, {DRM_MODE_CONNECTOR_DVID, QByteArrayLiteral("DVI-D")}, {DRM_MODE_CONNECTOR_DVIA, QByteArrayLiteral("DVI-A")}, {DRM_MODE_CONNECTOR_Composite, QByteArrayLiteral("Composite")}, {DRM_MODE_CONNECTOR_SVIDEO, QByteArrayLiteral("SVIDEO")}, {DRM_MODE_CONNECTOR_LVDS, QByteArrayLiteral("LVDS")}, {DRM_MODE_CONNECTOR_Component, QByteArrayLiteral("Component")}, {DRM_MODE_CONNECTOR_9PinDIN, QByteArrayLiteral("DIN")}, {DRM_MODE_CONNECTOR_DisplayPort, QByteArrayLiteral("DP")}, {DRM_MODE_CONNECTOR_HDMIA, QByteArrayLiteral("HDMI-A")}, {DRM_MODE_CONNECTOR_HDMIB, QByteArrayLiteral("HDMI-B")}, {DRM_MODE_CONNECTOR_TV, QByteArrayLiteral("TV")}, {DRM_MODE_CONNECTOR_eDP, QByteArrayLiteral("eDP")}, {DRM_MODE_CONNECTOR_VIRTUAL, QByteArrayLiteral("Virtual")}, {DRM_MODE_CONNECTOR_DSI, QByteArrayLiteral("DSI")}, #ifdef DRM_MODE_CONNECTOR_DPI {DRM_MODE_CONNECTOR_DPI, QByteArrayLiteral("DPI")}, #endif }; namespace { quint64 refreshRateForMode(_drmModeModeInfo *m) { // Calculate higher precision (mHz) refresh rate // logic based on Weston, see compositor-drm.c quint64 refreshRate = (m->clock * 1000000LL / m->htotal + m->vtotal / 2) / m->vtotal; if (m->flags & DRM_MODE_FLAG_INTERLACE) { refreshRate *= 2; } if (m->flags & DRM_MODE_FLAG_DBLSCAN) { refreshRate /= 2; } if (m->vscan > 1) { refreshRate /= m->vscan; } return refreshRate; } } bool DrmOutput::init(drmModeConnector *connector) { initEdid(connector); initDpms(connector); initUuid(); if (m_backend->atomicModeSetting()) { if (!initPrimaryPlane()) { return false; } } else if (!m_crtc->blank()) { return false; } setInternal(connector->connector_type == DRM_MODE_CONNECTOR_LVDS || connector->connector_type == DRM_MODE_CONNECTOR_eDP); setDpmsSupported(true); if (isInternal()) { connect(kwinApp(), &Application::screensCreated, this, [this] { connect(screens()->orientationSensor(), &OrientationSensor::orientationChanged, this, &DrmOutput::automaticRotation); } ); } QSize physicalSize = !m_edid.physicalSize.isEmpty() ? m_edid.physicalSize : QSize(connector->mmWidth, connector->mmHeight); // the size might be completely borked. E.g. Samsung SyncMaster 2494HS reports 160x90 while in truth it's 520x292 // as this information is used to calculate DPI info, it's going to result in everything being huge const QByteArray unknown = QByteArrayLiteral("unknown"); KConfigGroup group = kwinApp()->config()->group("EdidOverwrite").group(m_edid.eisaId.isEmpty() ? unknown : m_edid.eisaId) .group(m_edid.monitorName.isEmpty() ? unknown : m_edid.monitorName) .group(m_edid.serialNumber.isEmpty() ? unknown : m_edid.serialNumber); if (group.hasKey("PhysicalSize")) { const QSize overwriteSize = group.readEntry("PhysicalSize", physicalSize); qCWarning(KWIN_DRM) << "Overwriting monitor physical size for" << m_edid.eisaId << "/" << m_edid.monitorName << "/" << m_edid.serialNumber << " from " << physicalSize << "to " << overwriteSize; physicalSize = overwriteSize; } setRawPhysicalSize(physicalSize); initOutputDevice(connector); setEnabled(true); return true; } void DrmOutput::initUuid() { QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(QByteArray::number(m_conn->id())); hash.addData(m_edid.eisaId); hash.addData(m_edid.monitorName); hash.addData(m_edid.serialNumber); m_uuid = hash.result().toHex().left(10); } void DrmOutput::initOutputDevice(drmModeConnector *connector) { QString manufacturer; if (!m_edid.eisaId.isEmpty()) { manufacturer = QString::fromLatin1(m_edid.eisaId); } QString connectorName = s_connectorNames.value(connector->connector_type, QByteArrayLiteral("Unknown")); QString modelName; if (!m_edid.monitorName.isEmpty()) { QString m = QString::fromLatin1(m_edid.monitorName); if (!m_edid.serialNumber.isEmpty()) { m.append('/'); m.append(QString::fromLatin1(m_edid.serialNumber)); } modelName = m; } else if (!m_edid.serialNumber.isEmpty()) { modelName = QString::fromLatin1(m_edid.serialNumber); } else { modelName = i18n("unknown"); } const QString model = connectorName + QStringLiteral("-") + QString::number(connector->connector_type_id) + QStringLiteral("-") + modelName; // read in mode information QVector modes; for (int i = 0; i < connector->count_modes; ++i) { // TODO: in AMS here we could read and store for later every mode's blob_id // would simplify isCurrentMode(..) and presentAtomically(..) in case of mode set auto *m = &connector->modes[i]; KWayland::Server::OutputDeviceInterface::ModeFlags deviceflags; if (isCurrentMode(m)) { deviceflags |= KWayland::Server::OutputDeviceInterface::ModeFlag::Current; } if (m->type & DRM_MODE_TYPE_PREFERRED) { deviceflags |= KWayland::Server::OutputDeviceInterface::ModeFlag::Preferred; } KWayland::Server::OutputDeviceInterface::Mode mode; mode.id = i; mode.size = QSize(m->hdisplay, m->vdisplay); mode.flags = deviceflags; mode.refreshRate = refreshRateForMode(m); modes << mode; } - AbstractOutput::initWaylandOutputDevice(model, manufacturer, m_uuid, modes); + AbstractWaylandOutput::initWaylandOutputDevice(model, manufacturer, m_uuid, modes); } bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const { return mode->clock == m_mode.clock && mode->hdisplay == m_mode.hdisplay && mode->hsync_start == m_mode.hsync_start && mode->hsync_end == m_mode.hsync_end && mode->htotal == m_mode.htotal && mode->hskew == m_mode.hskew && mode->vdisplay == m_mode.vdisplay && mode->vsync_start == m_mode.vsync_start && mode->vsync_end == m_mode.vsync_end && mode->vtotal == m_mode.vtotal && mode->vscan == m_mode.vscan && mode->vrefresh == m_mode.vrefresh && mode->flags == m_mode.flags && mode->type == m_mode.type && qstrcmp(mode->name, m_mode.name) == 0; } static bool verifyEdidHeader(drmModePropertyBlobPtr edid) { const uint8_t *data = reinterpret_cast(edid->data); if (data[0] != 0x00) { return false; } for (int i = 1; i < 7; ++i) { if (data[i] != 0xFF) { return false; } } if (data[7] != 0x00) { return false; } return true; } static QByteArray extractEisaId(drmModePropertyBlobPtr edid) { /* * From EDID standard section 3.4: * The ID Manufacturer Name field, shown in Table 3.5, contains a 2-byte representation of the monitor's * manufacturer. This is the same as the EISA ID. It is based on compressed ASCII, “0001=A” ... “11010=Z”. * * The table: * | Byte | Bit | * | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * ---------------------------------------- * | 1 | 0)| (4| 3 | 2 | 1 | 0)| (4| 3 | * | | * | Character 1 | Char 2| * ---------------------------------------- * | 2 | 2 | 1 | 0)| (4| 3 | 2 | 1 | 0)| * | | Character2| Character 3 | * ---------------------------------------- **/ const uint8_t *data = reinterpret_cast(edid->data); static const uint offset = 0x8; char id[4]; if (data[offset] >> 7) { // bit at position 7 is not a 0 return QByteArray(); } // shift two bits to right, and with 7 right most bits id[0] = 'A' + ((data[offset] >> 2) & 0x1f) -1; // for first byte: take last two bits and shift them 3 to left (000xx000) // for second byte: shift 5 bits to right and take 3 right most bits (00000xxx) // or both together id[1] = 'A' + (((data[offset] & 0x3) << 3) | ((data[offset + 1] >> 5) & 0x7)) - 1; // take five right most bits id[2] = 'A' + (data[offset + 1] & 0x1f) - 1; id[3] = '\0'; return QByteArray(id); } static void extractMonitorDescriptorDescription(drmModePropertyBlobPtr blob, DrmOutput::Edid &edid) { // see section 3.10.3 const uint8_t *data = reinterpret_cast(blob->data); static const uint offset = 0x36; static const uint blockLength = 18; for (int i = 0; i < 5; ++i) { const uint co = offset + i * blockLength; // Flag = 0000h when block used as descriptor if (data[co] != 0) { continue; } if (data[co + 1] != 0) { continue; } // Reserved = 00h when block used as descriptor if (data[co + 2] != 0) { continue; } /* * FFh: Monitor Serial Number - Stored as ASCII, code page # 437, ≤ 13 bytes. * FEh: ASCII String - Stored as ASCII, code page # 437, ≤ 13 bytes. * FDh: Monitor range limits, binary coded * FCh: Monitor name, stored as ASCII, code page # 437 * FBh: Descriptor contains additional color point data * FAh: Descriptor contains additional Standard Timing Identifications * F9h - 11h: Currently undefined * 10h: Dummy descriptor, used to indicate that the descriptor space is unused * 0Fh - 00h: Descriptor defined by manufacturer. */ if (data[co + 3] == 0xfc && edid.monitorName.isEmpty()) { edid.monitorName = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); } if (data[co + 3] == 0xfe) { const QByteArray id = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); if (!id.isEmpty()) { edid.eisaId = id; } } if (data[co + 3] == 0xff) { edid.serialNumber = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); } } } static QByteArray extractSerialNumber(drmModePropertyBlobPtr edid) { // see section 3.4 const uint8_t *data = reinterpret_cast(edid->data); static const uint offset = 0x0C; /* * The ID serial number is a 32-bit serial number used to differentiate between individual instances of the same model * of monitor. Its use is optional. When used, the bit order for this field follows that shown in Table 3.6. The EDID * structure Version 1 Revision 1 and later offer a way to represent the serial number of the monitor as an ASCII string * in a separate descriptor block. */ uint32_t serialNumber = 0; serialNumber = (uint32_t) data[offset + 0]; serialNumber |= (uint32_t) data[offset + 1] << 8; serialNumber |= (uint32_t) data[offset + 2] << 16; serialNumber |= (uint32_t) data[offset + 3] << 24; if (serialNumber == 0) { return QByteArray(); } return QByteArray::number(serialNumber); } static QSize extractPhysicalSize(drmModePropertyBlobPtr edid) { const uint8_t *data = reinterpret_cast(edid->data); return QSize(data[0x15], data[0x16]) * 10; } void DrmOutput::initEdid(drmModeConnector *connector) { DrmScopedPointer edid; for (int i = 0; i < connector->count_props; ++i) { DrmScopedPointer property(drmModeGetProperty(m_backend->fd(), connector->props[i])); if (!property) { continue; } if ((property->flags & DRM_MODE_PROP_BLOB) && qstrcmp(property->name, "EDID") == 0) { edid.reset(drmModeGetPropertyBlob(m_backend->fd(), connector->prop_values[i])); } } if (!edid) { return; } // for documentation see: http://read.pudn.com/downloads110/ebook/456020/E-EDID%20Standard.pdf if (edid->length < 128) { return; } if (!verifyEdidHeader(edid.data())) { return; } m_edid.eisaId = extractEisaId(edid.data()); m_edid.serialNumber = extractSerialNumber(edid.data()); // parse monitor descriptor description extractMonitorDescriptorDescription(edid.data(), m_edid); m_edid.physicalSize = extractPhysicalSize(edid.data()); } bool DrmOutput::initPrimaryPlane() { for (int i = 0; i < m_backend->planes().size(); ++i) { DrmPlane* p = m_backend->planes()[i]; if (!p) { continue; } if (p->type() != DrmPlane::TypeIndex::Primary) { continue; } if (p->output()) { // Plane already has an output continue; } if (m_primaryPlane) { // Output already has a primary plane continue; } if (!p->isCrtcSupported(m_crtc->resIndex())) { continue; } p->setOutput(this); m_primaryPlane = p; qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtc->id(); return true; } qCCritical(KWIN_DRM) << "Failed to initialize primary plane."; return false; } bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first) { for (int i = 0; i < m_backend->planes().size(); ++i) { DrmPlane* p = m_backend->planes()[i]; if (!p) { continue; } if (p->type() != DrmPlane::TypeIndex::Cursor) { continue; } if (p->output()) { // Plane already has an output continue; } if (m_cursorPlane) { // Output already has a cursor plane continue; } if (!p->isCrtcSupported(m_crtc->resIndex())) { continue; } p->setOutput(this); m_cursorPlane = p; qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtc->id(); return true; } return false; } bool DrmOutput::initCursor(const QSize &cursorSize) { auto createCursor = [this, cursorSize] (int index) { m_cursor[index] = m_backend->createBuffer(cursorSize); if (!m_cursor[index]->map(QImage::Format_ARGB32_Premultiplied)) { return false; } return true; }; if (!createCursor(0) || !createCursor(1)) { return false; } return true; } void DrmOutput::initDpms(drmModeConnector *connector) { for (int i = 0; i < connector->count_props; ++i) { DrmScopedPointer property(drmModeGetProperty(m_backend->fd(), connector->props[i])); if (!property) { continue; } if (qstrcmp(property->name, "DPMS") == 0) { m_dpms.swap(property); break; } } } static DrmOutput::DpmsMode fromWaylandDpmsMode(KWayland::Server::OutputInterface::DpmsMode wlMode) { using namespace KWayland::Server; switch (wlMode) { case OutputInterface::DpmsMode::On: return DrmOutput::DpmsMode::On; case OutputInterface::DpmsMode::Standby: return DrmOutput::DpmsMode::Standby; case OutputInterface::DpmsMode::Suspend: return DrmOutput::DpmsMode::Suspend; case OutputInterface::DpmsMode::Off: return DrmOutput::DpmsMode::Off; default: Q_UNREACHABLE(); } } static KWayland::Server::OutputInterface::DpmsMode toWaylandDpmsMode(DrmOutput::DpmsMode mode) { using namespace KWayland::Server; switch (mode) { case DrmOutput::DpmsMode::On: return OutputInterface::DpmsMode::On; case DrmOutput::DpmsMode::Standby: return OutputInterface::DpmsMode::Standby; case DrmOutput::DpmsMode::Suspend: return OutputInterface::DpmsMode::Suspend; case DrmOutput::DpmsMode::Off: return OutputInterface::DpmsMode::Off; default: Q_UNREACHABLE(); } } void DrmOutput::updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) { if (m_dpms.isNull()) { return; } const auto drmMode = fromWaylandDpmsMode(mode); if (drmMode == m_dpmsModePending) { qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; return; } m_dpmsModePending = drmMode; if (m_backend->atomicModeSetting()) { m_modesetRequested = true; if (drmMode == DpmsMode::On) { if (m_pageFlipPending) { m_pageFlipPending = false; Compositor::self()->bufferSwapComplete(); } dpmsOnHandler(); } else { m_dpmsAtomicOffPending = true; if (!m_pageFlipPending) { dpmsAtomicOff(); } } } else { if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(drmMode)) < 0) { m_dpmsModePending = m_dpmsMode; qCWarning(KWIN_DRM) << "Setting DPMS failed"; return; } if (drmMode == DpmsMode::On) { dpmsOnHandler(); } else { dpmsOffHandler(); } m_dpmsMode = m_dpmsModePending; } } void DrmOutput::dpmsOnHandler() { qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to On."; auto wlOutput = waylandOutput(); if (wlOutput) { wlOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); } emit dpmsChanged(); m_backend->checkOutputsAreOn(); if (!m_backend->atomicModeSetting()) { m_crtc->blank(); } if (Compositor *compositor = Compositor::self()) { compositor->addRepaintFull(); } } void DrmOutput::dpmsOffHandler() { qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to Off."; auto wlOutput = waylandOutput(); if (wlOutput) { wlOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); } emit dpmsChanged(); m_backend->outputWentOff(); } void DrmOutput::transform(KWayland::Server::OutputDeviceInterface::Transform transform) { waylandOutputDevice()->setTransform(transform); using KWayland::Server::OutputDeviceInterface; using KWayland::Server::OutputInterface; auto wlOutput = waylandOutput(); switch (transform) { case OutputDeviceInterface::Transform::Normal: if (m_primaryPlane) { m_primaryPlane->setTransformation(DrmPlane::Transformation::Rotate0); } if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Normal); } setOrientation(Qt::PrimaryOrientation); break; case OutputDeviceInterface::Transform::Rotated90: if (m_primaryPlane) { m_primaryPlane->setTransformation(DrmPlane::Transformation::Rotate90); } if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Rotated90); } setOrientation(Qt::PortraitOrientation); break; case OutputDeviceInterface::Transform::Rotated180: if (m_primaryPlane) { m_primaryPlane->setTransformation(DrmPlane::Transformation::Rotate180); } if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Rotated180); } setOrientation(Qt::InvertedLandscapeOrientation); break; case OutputDeviceInterface::Transform::Rotated270: if (m_primaryPlane) { m_primaryPlane->setTransformation(DrmPlane::Transformation::Rotate270); } if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Rotated270); } setOrientation(Qt::InvertedPortraitOrientation); break; case OutputDeviceInterface::Transform::Flipped: // TODO: what is this exactly? if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Flipped); } break; case OutputDeviceInterface::Transform::Flipped90: // TODO: what is this exactly? if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Flipped90); } break; case OutputDeviceInterface::Transform::Flipped180: // TODO: what is this exactly? if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Flipped180); } break; case OutputDeviceInterface::Transform::Flipped270: // TODO: what is this exactly? if (wlOutput) { wlOutput->setTransform(OutputInterface::Transform::Flipped270); } break; } m_modesetRequested = true; // the cursor might need to get rotated updateCursor(); showCursor(); // TODO: are these calls not enough in updateMode already? setWaylandMode(); } void DrmOutput::updateMode(int modeIndex) { // get all modes on the connector DrmScopedPointer connector(drmModeGetConnector(m_backend->fd(), m_conn->id())); if (connector->count_modes <= modeIndex) { // TODO: error? return; } if (isCurrentMode(&connector->modes[modeIndex])) { // nothing to do return; } m_mode = connector->modes[modeIndex]; m_modesetRequested = true; setWaylandMode(); } QSize DrmOutput::pixelSize() const { return orientateSize(QSize(m_mode.hdisplay, m_mode.vdisplay)); } void DrmOutput::setWaylandMode() { - AbstractOutput::setWaylandMode(QSize(m_mode.hdisplay, m_mode.vdisplay), - refreshRateForMode(&m_mode)); + AbstractWaylandOutput::setWaylandMode(QSize(m_mode.hdisplay, m_mode.vdisplay), + refreshRateForMode(&m_mode)); } void DrmOutput::pageFlipped() { m_pageFlipPending = false; if (m_deleted) { deleteLater(); return; } if (!m_crtc) { return; } // Egl based surface buffers get destroyed, QPainter based dumb buffers not // TODO: split up DrmOutput in two for dumb and egl/gbm surface buffer compatible subclasses completely? if (m_backend->deleteBufferAfterPageFlip()) { if (m_backend->atomicModeSetting()) { if (!m_primaryPlane->next()) { // on manual vt switch // TODO: when we later use overlay planes it might happen, that we have a page flip with only // damage on one of these, and therefore the primary plane has no next buffer // -> Then we don't want to return here! if (m_primaryPlane->current()) { m_primaryPlane->current()->releaseGbm(); } return; } for (DrmPlane *p : m_nextPlanesFlipList) { p->flipBufferWithDelete(); } m_nextPlanesFlipList.clear(); } else { if (!m_crtc->next()) { // on manual vt switch if (DrmBuffer *b = m_crtc->current()) { b->releaseGbm(); } } m_crtc->flipBuffer(); } } else { if (m_backend->atomicModeSetting()){ for (DrmPlane *p : m_nextPlanesFlipList) { p->flipBuffer(); } m_nextPlanesFlipList.clear(); } else { m_crtc->flipBuffer(); } m_crtc->flipBuffer(); } } bool DrmOutput::present(DrmBuffer *buffer) { if (m_backend->atomicModeSetting()) { return presentAtomically(buffer); } else { return presentLegacy(buffer); } } bool DrmOutput::dpmsAtomicOff() { m_dpmsAtomicOffPending = false; // TODO: With multiple planes: deactivate all of them here delete m_primaryPlane->next(); m_primaryPlane->setNext(nullptr); m_nextPlanesFlipList << m_primaryPlane; if (!doAtomicCommit(AtomicCommitMode::Test)) { qCDebug(KWIN_DRM) << "Atomic test commit to Dpms Off failed. Aborting."; return false; } if (!doAtomicCommit(AtomicCommitMode::Real)) { qCDebug(KWIN_DRM) << "Atomic commit to Dpms Off failed. This should have never happened! Aborting."; return false; } m_nextPlanesFlipList.clear(); dpmsOffHandler(); return true; } bool DrmOutput::presentAtomically(DrmBuffer *buffer) { if (!LogindIntegration::self()->isActiveSession()) { qCWarning(KWIN_DRM) << "Logind session not active."; return false; } if (m_pageFlipPending) { qCWarning(KWIN_DRM) << "Page not yet flipped."; return false; } #if HAVE_EGL_STREAMS if (m_backend->useEglStreams() && !m_modesetRequested) { // EglStreamBackend queues normal page flips through EGL, // modesets are still performed through DRM-KMS m_pageFlipPending = true; return true; } #endif m_primaryPlane->setNext(buffer); m_nextPlanesFlipList << m_primaryPlane; if (!doAtomicCommit(AtomicCommitMode::Test)) { //TODO: When we use planes for layered rendering, fallback to renderer instead. Also for direct scanout? //TODO: Probably should undo setNext and reset the flip list qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present."; // go back to previous state if (m_lastWorkingState.valid) { m_mode = m_lastWorkingState.mode; setOrientation(m_lastWorkingState.orientation); setGlobalPos(m_lastWorkingState.globalPos); if (m_primaryPlane) { m_primaryPlane->setTransformation(m_lastWorkingState.planeTransformations); } m_modesetRequested = true; // the cursor might need to get rotated updateCursor(); showCursor(); // TODO: forward to OutputInterface and OutputDeviceInterface setWaylandMode(); emit screens()->changed(); } return false; } const bool wasModeset = m_modesetRequested; if (!doAtomicCommit(AtomicCommitMode::Real)) { qCDebug(KWIN_DRM) << "Atomic commit failed. This should have never happened! Aborting present."; //TODO: Probably should undo setNext and reset the flip list return false; } if (wasModeset) { // store current mode set as new good state m_lastWorkingState.mode = m_mode; m_lastWorkingState.orientation = orientation(); m_lastWorkingState.globalPos = globalPos(); if (m_primaryPlane) { m_lastWorkingState.planeTransformations = m_primaryPlane->transformation(); } m_lastWorkingState.valid = true; } m_pageFlipPending = true; return true; } bool DrmOutput::presentLegacy(DrmBuffer *buffer) { if (m_crtc->next()) { return false; } if (!LogindIntegration::self()->isActiveSession()) { m_crtc->setNext(buffer); return false; } if (m_dpmsMode != DpmsMode::On) { return false; } // Do we need to set a new mode first? if (!m_crtc->current() || m_crtc->current()->needsModeChange(buffer)) { if (!setModeLegacy(buffer)) { return false; } } const bool ok = drmModePageFlip(m_backend->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; if (ok) { m_crtc->setNext(buffer); } else { qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); } return ok; } bool DrmOutput::setModeLegacy(DrmBuffer *buffer) { uint32_t connId = m_conn->id(); if (drmModeSetCrtc(m_backend->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) { return true; } else { qCWarning(KWIN_DRM) << "Mode setting failed"; return false; } } bool DrmOutput::doAtomicCommit(AtomicCommitMode mode) { drmModeAtomicReq *req = drmModeAtomicAlloc(); auto errorHandler = [this, mode, req] () { if (mode == AtomicCommitMode::Test) { // TODO: when we later test overlay planes, make sure we change only the right stuff back } if (req) { drmModeAtomicFree(req); } if (m_dpmsMode != m_dpmsModePending) { qCWarning(KWIN_DRM) << "Setting DPMS failed"; m_dpmsModePending = m_dpmsMode; if (m_dpmsMode != DpmsMode::On) { dpmsOffHandler(); } } // TODO: see above, rework later for overlay planes! for (DrmPlane *p : m_nextPlanesFlipList) { p->setNext(nullptr); } m_nextPlanesFlipList.clear(); }; if (!req) { qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; errorHandler(); return false; } uint32_t flags = 0; // Do we need to set a new mode? if (m_modesetRequested) { if (m_dpmsModePending == DpmsMode::On) { if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) { qCWarning(KWIN_DRM) << "Failed to create property blob"; errorHandler(); return false; } } if (!atomicReqModesetPopulate(req, m_dpmsModePending == DpmsMode::On)){ qCWarning(KWIN_DRM) << "Failed to populate Atomic Modeset"; errorHandler(); return false; } flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } if (mode == AtomicCommitMode::Real) { if (m_dpmsModePending == DpmsMode::On) { if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { // TODO: Evaluating this condition should only be necessary, as long as we expect older kernels than 4.10. flags |= DRM_MODE_ATOMIC_NONBLOCK; } #if HAVE_EGL_STREAMS if (!m_backend->useEglStreams()) // EglStreamBackend uses the NV_output_drm_flip_event EGL extension // to register the flip event through eglStreamConsumerAcquireAttribNV #endif flags |= DRM_MODE_PAGE_FLIP_EVENT; } } else { flags |= DRM_MODE_ATOMIC_TEST_ONLY; } bool ret = true; // TODO: Make sure when we use more than one plane at a time, that we go through this list in the right order. for (int i = m_nextPlanesFlipList.size() - 1; 0 <= i; i-- ) { DrmPlane *p = m_nextPlanesFlipList[i]; ret &= p->atomicPopulate(req); } if (!ret) { qCWarning(KWIN_DRM) << "Failed to populate atomic planes. Abort atomic commit!"; errorHandler(); return false; } if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) { qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno); errorHandler(); return false; } if (mode == AtomicCommitMode::Real && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { qCDebug(KWIN_DRM) << "Atomic Modeset successful."; m_modesetRequested = false; m_dpmsMode = m_dpmsModePending; } drmModeAtomicFree(req); return true; } bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) { if (enable) { m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id()); } else { if (m_backend->deleteBufferAfterPageFlip()) { delete m_primaryPlane->current(); delete m_primaryPlane->next(); } m_primaryPlane->setCurrent(nullptr); m_primaryPlane->setNext(nullptr); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), 0); m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), 0); } m_conn->setValue(int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0); m_crtc->setValue(int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0); m_crtc->setValue(int(DrmCrtc::PropertyIndex::Active), enable); bool ret = true; ret &= m_conn->atomicPopulate(req); ret &= m_crtc->atomicPopulate(req); return ret; } bool DrmOutput::supportsTransformations() const { if (!m_primaryPlane) { return false; } const auto transformations = m_primaryPlane->supportedTransformations(); return transformations.testFlag(DrmPlane::Transformation::Rotate90) || transformations.testFlag(DrmPlane::Transformation::Rotate180) || transformations.testFlag(DrmPlane::Transformation::Rotate270); } void DrmOutput::automaticRotation() { if (!m_primaryPlane) { return; } const auto supportedTransformations = m_primaryPlane->supportedTransformations(); const auto requestedTransformation = screens()->orientationSensor()->orientation(); using KWayland::Server::OutputDeviceInterface; OutputDeviceInterface::Transform newTransformation = OutputDeviceInterface::Transform::Normal; switch (requestedTransformation) { case OrientationSensor::Orientation::TopUp: newTransformation = OutputDeviceInterface::Transform::Normal; break; case OrientationSensor::Orientation::TopDown: if (!supportedTransformations.testFlag(DrmPlane::Transformation::Rotate180)) { return; } newTransformation = OutputDeviceInterface::Transform::Rotated180; break; case OrientationSensor::Orientation::LeftUp: if (!supportedTransformations.testFlag(DrmPlane::Transformation::Rotate90)) { return; } newTransformation = OutputDeviceInterface::Transform::Rotated90; break; case OrientationSensor::Orientation::RightUp: if (!supportedTransformations.testFlag(DrmPlane::Transformation::Rotate270)) { return; } newTransformation = OutputDeviceInterface::Transform::Rotated270; break; case OrientationSensor::Orientation::FaceUp: case OrientationSensor::Orientation::FaceDown: case OrientationSensor::Orientation::Undefined: // unsupported return; } transform(newTransformation); emit screens()->changed(); } int DrmOutput::getGammaRampSize() const { return m_crtc->getGammaRampSize(); } bool DrmOutput::setGammaRamp(const ColorCorrect::GammaRamp &gamma) { return m_crtc->setGammaRamp(gamma); } } diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h index bc77d8991..c2199e677 100644 --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -1,177 +1,177 @@ /******************************************************************** 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_DRM_OUTPUT_H #define KWIN_DRM_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include "drm_pointer.h" #include "drm_object.h" #include "drm_object_plane.h" #include #include #include #include #include namespace KWin { class DrmBackend; class DrmBuffer; class DrmDumbBuffer; class DrmPlane; class DrmConnector; class DrmCrtc; -class KWIN_EXPORT DrmOutput : public AbstractOutput +class KWIN_EXPORT DrmOutput : public AbstractWaylandOutput { Q_OBJECT public: struct Edid { QByteArray eisaId; QByteArray monitorName; QByteArray serialNumber; QSize physicalSize; }; ///deletes the output, calling this whilst a page flip is pending will result in an error ~DrmOutput() override; ///queues deleting the output after a page flip has completed. void teardown(); void releaseGbm(); bool showCursor(DrmDumbBuffer *buffer); bool showCursor(); bool hideCursor(); void updateCursor(); void moveCursor(const QPoint &globalPos); bool init(drmModeConnector *connector); bool present(DrmBuffer *buffer); void pageFlipped(); QSize pixelSize() const override; // These values are defined by the kernel enum class DpmsMode { On = DRM_MODE_DPMS_ON, Standby = DRM_MODE_DPMS_STANDBY, Suspend = DRM_MODE_DPMS_SUSPEND, Off = DRM_MODE_DPMS_OFF }; bool isDpmsEnabled() const { // We care for current as well as pending mode in order to allow first present in AMS. return m_dpmsModePending == DpmsMode::On; } QByteArray uuid() const { return m_uuid; } const DrmCrtc *crtc() const { return m_crtc; } const DrmPlane *primaryPlane() const { return m_primaryPlane; } bool initCursor(const QSize &cursorSize); bool supportsTransformations() const; Q_SIGNALS: void dpmsChanged(); private: friend class DrmBackend; friend class DrmCrtc; // TODO: For use of setModeLegacy. Remove later when we allow multiple connectors per crtc // and save the connector ids in the DrmCrtc instance. DrmOutput(DrmBackend *backend); bool presentAtomically(DrmBuffer *buffer); enum class AtomicCommitMode { Test, Real }; bool doAtomicCommit(AtomicCommitMode mode); bool presentLegacy(DrmBuffer *buffer); bool setModeLegacy(DrmBuffer *buffer); void initEdid(drmModeConnector *connector); void initDpms(drmModeConnector *connector); void initOutputDevice(drmModeConnector *connector); bool isCurrentMode(const drmModeModeInfo *mode) const; void initUuid(); bool initPrimaryPlane(); bool initCursorPlane(); void dpmsOnHandler(); void dpmsOffHandler(); bool dpmsAtomicOff(); bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) override; void updateMode(int modeIndex) override; void setWaylandMode(); void transform(KWayland::Server::OutputDeviceInterface::Transform transform) override; void automaticRotation(); int getGammaRampSize() const override; bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) override; QMatrix4x4 matrixDisplay(const QSize &s) const; DrmBackend *m_backend; DrmConnector *m_conn = nullptr; DrmCrtc *m_crtc = nullptr; bool m_lastGbm = false; drmModeModeInfo m_mode; Edid m_edid; DrmScopedPointer m_dpms; DpmsMode m_dpmsMode = DpmsMode::On; DpmsMode m_dpmsModePending = DpmsMode::On; QByteArray m_uuid; uint32_t m_blobId = 0; DrmPlane* m_primaryPlane = nullptr; DrmPlane* m_cursorPlane = nullptr; QVector m_nextPlanesFlipList; bool m_pageFlipPending = false; bool m_dpmsAtomicOffPending = false; bool m_modesetRequested = true; struct { Qt::ScreenOrientation orientation; drmModeModeInfo mode; DrmPlane::Transformations planeTransformations; QPoint globalPos; bool valid = false; } m_lastWorkingState; DrmDumbBuffer *m_cursor[2] = {nullptr, nullptr}; int m_cursorIndex = 0; bool m_hasNewCursor = false; bool m_deleted = false; }; } Q_DECLARE_METATYPE(KWin::DrmOutput*) #endif diff --git a/plugins/platforms/fbdev/fb_backend.h b/plugins/platforms/fbdev/fb_backend.h index 8b7c8ab43..666cf3f16 100644 --- a/plugins/platforms/fbdev/fb_backend.h +++ b/plugins/platforms/fbdev/fb_backend.h @@ -1,131 +1,131 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2015 Martin Gräßlin Copyright 2019 Roman Gilg 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_FB_BACKEND_H #define KWIN_FB_BACKEND_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include "platform.h" #include #include namespace KWin { -class FramebufferOutput : public AbstractOutput +class FramebufferOutput : public AbstractWaylandOutput { Q_OBJECT public: - FramebufferOutput(QObject *parent = nullptr) : AbstractOutput(parent) {} + FramebufferOutput(QObject *parent = nullptr) : AbstractWaylandOutput(parent) {} virtual ~FramebufferOutput() = default; QSize pixelSize() const override { return m_pixelSize; } void setPixelSize(const QSize &set) { m_pixelSize = set; } void setRawPhysicalSize(const QSize &set) { - AbstractOutput::setRawPhysicalSize(set); + AbstractWaylandOutput::setRawPhysicalSize(set); } private: QSize m_pixelSize; }; class KWIN_EXPORT FramebufferBackend : public Platform { Q_OBJECT Q_INTERFACES(KWin::Platform) Q_PLUGIN_METADATA(IID "org.kde.kwin.Platform" FILE "fbdev.json") public: explicit FramebufferBackend(QObject *parent = nullptr); virtual ~FramebufferBackend(); Screens *createScreens(QObject *parent = nullptr) override; QPainterBackend *createQPainterBackend() override; QSize screenSize() const override; void init() override; bool isValid() const { return m_fd >= 0; } void map(); void unmap(); void *mappedMemory() const { return m_memory; } int bytesPerLine() const { return m_bytesPerLine; } int bufferSize() const { return m_bufferLength; } quint32 bitsPerPixel() const { return m_bitsPerPixel; } QImage::Format imageFormat() const; /** * @returns whether the imageFormat is BGR instead of RGB. **/ bool isBGR() const { return m_bgr; } Outputs outputs() const override; Outputs enabledOutputs() const override; QVector supportedCompositors() const override { return QVector{QPainterCompositing}; } private: void openFrameBuffer(); bool handleScreenInfo(); void initImageFormat(); QVector m_outputs; QByteArray m_id; struct Color { quint32 offset; quint32 length; }; Color m_red; Color m_green; Color m_blue; Color m_alpha; quint32 m_bitsPerPixel = 0; int m_fd = -1; quint32 m_bufferLength = 0; int m_bytesPerLine = 0; void *m_memory = nullptr; QImage::Format m_imageFormat = QImage::Format_Invalid; bool m_bgr = false; }; } #endif diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp index da1ca11bd..58a9b88c0 100644 --- a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp @@ -1,557 +1,557 @@ /******************************************************************** 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 3 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 "egl_hwcomposer_backend.h" #include "hwcomposer_backend.h" #include "logging.h" #include "screens_hwcomposer.h" #include "composite.h" #include "input.h" #include "main.h" #include "wayland_server.h" // KWayland #include // KDE #include // Qt #include #include // hybris/android #include #include // linux #include // based on test_hwcomposer.c from libhybris project (Apache 2 licensed) using namespace KWayland::Server; namespace KWin { BacklightInputEventFilter::BacklightInputEventFilter(HwcomposerBackend *backend) : InputEventFilter() , m_backend(backend) { } BacklightInputEventFilter::~BacklightInputEventFilter() = default; bool BacklightInputEventFilter::pointerEvent(QMouseEvent *event, quint32 nativeButton) { Q_UNUSED(event) Q_UNUSED(nativeButton) if (!m_backend->isBacklightOff()) { return false; } toggleBacklight(); return true; } bool BacklightInputEventFilter::wheelEvent(QWheelEvent *event) { Q_UNUSED(event) if (!m_backend->isBacklightOff()) { return false; } toggleBacklight(); return true; } bool BacklightInputEventFilter::keyEvent(QKeyEvent *event) { if (event->key() == Qt::Key_PowerOff && event->type() == QEvent::KeyRelease) { toggleBacklight(); return true; } return m_backend->isBacklightOff(); } bool BacklightInputEventFilter::touchDown(quint32 id, const QPointF &pos, quint32 time) { Q_UNUSED(pos) Q_UNUSED(time) if (!m_backend->isBacklightOff()) { return false; } if (m_touchPoints.isEmpty()) { if (!m_doubleTapTimer.isValid()) { // this is the first tap m_doubleTapTimer.start(); } else { if (m_doubleTapTimer.elapsed() < qApp->doubleClickInterval()) { m_secondTap = true; } else { // took too long. Let's consider it a new click m_doubleTapTimer.restart(); } } } else { // not a double tap m_doubleTapTimer.invalidate(); m_secondTap = false; } m_touchPoints << id; return true; } bool BacklightInputEventFilter::touchUp(quint32 id, quint32 time) { Q_UNUSED(time) m_touchPoints.removeAll(id); if (!m_backend->isBacklightOff()) { return false; } if (m_touchPoints.isEmpty() && m_doubleTapTimer.isValid() && m_secondTap) { if (m_doubleTapTimer.elapsed() < qApp->doubleClickInterval()) { toggleBacklight(); } m_doubleTapTimer.invalidate(); m_secondTap = false; } return true; } bool BacklightInputEventFilter::touchMotion(quint32 id, const QPointF &pos, quint32 time) { Q_UNUSED(id) Q_UNUSED(pos) Q_UNUSED(time) return m_backend->isBacklightOff(); } void BacklightInputEventFilter::toggleBacklight() { // queued to not modify the list of event filters while filtering QMetaObject::invokeMethod(m_backend, "toggleBlankOutput", Qt::QueuedConnection); } HwcomposerBackend::HwcomposerBackend(QObject *parent) : Platform(parent) { if (!QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.Solid.PowerManagement"), QStringLiteral("/org/kde/Solid/PowerManagement/Actions/BrightnessControl"), QStringLiteral("org.kde.Solid.PowerManagement.Actions.BrightnessControl"), QStringLiteral("brightnessChanged"), this, SLOT(screenBrightnessChanged(int)))) { qCWarning(KWIN_HWCOMPOSER) << "Failed to connect to brightness control"; } handleOutputs(); } HwcomposerBackend::~HwcomposerBackend() { if (!m_outputBlank) { toggleBlankOutput(); } } void HwcomposerBackend::init() { hw_module_t *hwcModule = nullptr; if (hw_get_module(HWC_HARDWARE_MODULE_ID, (const hw_module_t **)&hwcModule) != 0) { qCWarning(KWIN_HWCOMPOSER) << "Failed to get hwcomposer module"; emit initFailed(); return; } hwc_composer_device_1_t *hwcDevice = nullptr; if (hwc_open_1(hwcModule, &hwcDevice) != 0) { qCWarning(KWIN_HWCOMPOSER) << "Failed to open hwcomposer device"; emit initFailed(); return; } // unblank, setPowerMode? m_device = hwcDevice; m_hwcVersion = m_device->common.version; if ((m_hwcVersion & 0xffff0000) == 0) { // Assume header version is always 1 uint32_t header_version = 1; // Legacy version encoding m_hwcVersion = (m_hwcVersion << 16) | header_version; } // register callbacks hwc_procs_t *procs = new hwc_procs_t; procs->invalidate = [] (const struct hwc_procs* procs) { Q_UNUSED(procs) }; procs->vsync = [] (const struct hwc_procs* procs, int disp, int64_t timestamp) { Q_UNUSED(procs) if (disp != 0) { return; } dynamic_cast(kwinApp()->platform())->wakeVSync(); }; procs->hotplug = [] (const struct hwc_procs* procs, int disp, int connected) { Q_UNUSED(procs) Q_UNUSED(disp) Q_UNUSED(connected) }; m_device->registerProcs(m_device, procs); //move to HwcomposerOutput + signal initLights(); toggleBlankOutput(); m_filter.reset(new BacklightInputEventFilter(this)); input()->prependInputEventFilter(m_filter.data()); // get display configuration m_output.reset(new HwcomposerOutput(hwcDevice)); if (!m_output->isValid()) { emit initFailed(); return; } if (m_output->refreshRate() != 0) { m_vsyncInterval = 1000000/m_output->refreshRate(); } if (m_lights) { using namespace KWayland::Server; auto updateDpms = [this] { if (!m_output || !m_output->waylandOutput()) { m_output->waylandOutput()->setDpmsMode(m_outputBlank ? OutputInterface::DpmsMode::Off : OutputInterface::DpmsMode::On); } }; connect(this, &HwcomposerBackend::outputBlankChanged, this, updateDpms); connect(m_output.data(), &HwcomposerOutput::dpmsModeRequested, this, [this] (KWayland::Server::OutputInterface::DpmsMode mode) { if (mode == OutputInterface::DpmsMode::On) { if (m_outputBlank) { toggleBlankOutput(); } } else { if (!m_outputBlank) { toggleBlankOutput(); } } } ); } emit screensQueried(); setReady(true); } QSize HwcomposerBackend::size() const { if (m_output) { return m_output->pixelSize(); } return QSize(); } QSize HwcomposerBackend::screenSize() const { if (m_output) { return m_output->pixelSize() / m_output->scale(); } return QSize(); } int HwcomposerBackend::scale() const { if (m_output) { return m_output->scale(); } return 1; } void HwcomposerBackend::initLights() { hw_module_t *lightsModule = nullptr; if (hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (const hw_module_t **)&lightsModule) != 0) { qCWarning(KWIN_HWCOMPOSER) << "Failed to get lights module"; return; } light_device_t *lightsDevice = nullptr; if (lightsModule->methods->open(lightsModule, LIGHT_ID_BACKLIGHT, (hw_device_t **)&lightsDevice) != 0) { qCWarning(KWIN_HWCOMPOSER) << "Failed to create lights device"; return; } m_lights = lightsDevice; } void HwcomposerBackend::toggleBlankOutput() { if (!m_device) { return; } m_outputBlank = !m_outputBlank; toggleScreenBrightness(); #if defined(HWC_DEVICE_API_VERSION_1_4) || defined(HWC_DEVICE_API_VERSION_1_5) if (m_hwcVersion > HWC_DEVICE_API_VERSION_1_3) m_device->setPowerMode(m_device, 0, m_outputBlank ? HWC_POWER_MODE_OFF : HWC_POWER_MODE_NORMAL); else #endif m_device->blank(m_device, 0, m_outputBlank ? 1 : 0); // only disable Vsync, enable happens after next frame rendered if (m_outputBlank) { enableVSync(false); } // enable/disable compositor repainting when blanked setOutputsEnabled(!m_outputBlank); if (Compositor *compositor = Compositor::self()) { if (!m_outputBlank) { compositor->addRepaintFull(); } } emit outputBlankChanged(); } void HwcomposerBackend::toggleScreenBrightness() { if (!m_lights) { return; } const int brightness = m_outputBlank ? 0 : m_oldScreenBrightness; struct light_state_t state; state.flashMode = LIGHT_FLASH_NONE; state.brightnessMode = BRIGHTNESS_MODE_USER; state.color = (int)((0xffU << 24) | (brightness << 16) | (brightness << 8) | brightness); m_lights->set_light(m_lights, &state); } void HwcomposerBackend::enableVSync(bool enable) { if (m_hasVsync == enable) { return; } const int result = m_device->eventControl(m_device, 0, HWC_EVENT_VSYNC, enable ? 1: 0); m_hasVsync = enable && (result == 0); } HwcomposerWindow *HwcomposerBackend::createSurface() { return new HwcomposerWindow(this); } Screens *HwcomposerBackend::createScreens(QObject *parent) { return new HwcomposerScreens(this, parent); } Outputs HwcomposerBackend::outputs() const { if (!m_output.isNull()) { return QVector({m_output.data()}); } return {}; } Outputs HwcomposerBackend::enabledOutputs() const { return outputs(); } OpenGLBackend *HwcomposerBackend::createOpenGLBackend() { return new EglHwcomposerBackend(this); } void HwcomposerBackend::waitVSync() { if (!m_hasVsync) { return; } m_vsyncMutex.lock(); m_vsyncWaitCondition.wait(&m_vsyncMutex, m_vsyncInterval); m_vsyncMutex.unlock(); } void HwcomposerBackend::wakeVSync() { m_vsyncMutex.lock(); m_vsyncWaitCondition.wakeAll(); m_vsyncMutex.unlock(); } static void initLayer(hwc_layer_1_t *layer, const hwc_rect_t &rect, int layerCompositionType) { memset(layer, 0, sizeof(hwc_layer_1_t)); layer->compositionType = layerCompositionType; layer->hints = 0; layer->flags = 0; layer->handle = 0; layer->transform = 0; layer->blending = HWC_BLENDING_NONE; #ifdef HWC_DEVICE_API_VERSION_1_3 layer->sourceCropf.top = 0.0f; layer->sourceCropf.left = 0.0f; layer->sourceCropf.bottom = (float) rect.bottom; layer->sourceCropf.right = (float) rect.right; #else layer->sourceCrop = rect; #endif layer->displayFrame = rect; layer->visibleRegionScreen.numRects = 1; layer->visibleRegionScreen.rects = &layer->displayFrame; layer->acquireFenceFd = -1; layer->releaseFenceFd = -1; layer->planeAlpha = 0xFF; #ifdef HWC_DEVICE_API_VERSION_1_5 layer->surfaceDamage.numRects = 0; #endif } HwcomposerWindow::HwcomposerWindow(HwcomposerBackend *backend) : HWComposerNativeWindow(backend->size().width(), backend->size().height(), HAL_PIXEL_FORMAT_RGBA_8888) , m_backend(backend) { setBufferCount(3); size_t size = sizeof(hwc_display_contents_1_t) + 2 * sizeof(hwc_layer_1_t); hwc_display_contents_1_t *list = (hwc_display_contents_1_t*)malloc(size); m_list = (hwc_display_contents_1_t**)malloc(HWC_NUM_DISPLAY_TYPES * sizeof(hwc_display_contents_1_t *)); for (int i = 0; i < HWC_NUM_DISPLAY_TYPES; ++i) { m_list[i] = nullptr; } // Assign buffer only to the first item, otherwise you get tearing // if passed the same to multiple places // see https://github.com/mer-hybris/qt5-qpa-hwcomposer-plugin/commit/f1d802151e8a4f5d10d60eb8de8e07552b93a34a m_list[0] = list; const hwc_rect_t rect = { 0, 0, m_backend->size().width(), m_backend->size().height() }; initLayer(&list->hwLayers[0], rect, HWC_FRAMEBUFFER); initLayer(&list->hwLayers[1], rect, HWC_FRAMEBUFFER_TARGET); list->retireFenceFd = -1; list->flags = HWC_GEOMETRY_CHANGED; list->numHwLayers = 2; } HwcomposerWindow::~HwcomposerWindow() { // TODO: cleanup } void HwcomposerWindow::present(HWComposerNativeWindowBuffer *buffer) { m_backend->waitVSync(); hwc_composer_device_1_t *device = m_backend->device(); auto fblayer = &m_list[0]->hwLayers[1]; fblayer->handle = buffer->handle; fblayer->acquireFenceFd = getFenceBufferFd(buffer); fblayer->releaseFenceFd = -1; int err = device->prepare(device, 1, m_list); assert(err == 0); err = device->set(device, 1, m_list); assert(err == 0); m_backend->enableVSync(true); setFenceBufferFd(buffer, fblayer->releaseFenceFd); if (m_list[0]->retireFenceFd != -1) { close(m_list[0]->retireFenceFd); m_list[0]->retireFenceFd = -1; } m_list[0]->flags = 0; } HwcomposerOutput::HwcomposerOutput(hwc_composer_device_1_t *device) - : AbstractOutput() + : AbstractWaylandOutput() , m_device(device) { uint32_t configs[5]; size_t numConfigs = 5; if (device->getDisplayConfigs(device, 0, configs, &numConfigs) != 0) { qCWarning(KWIN_HWCOMPOSER) << "Failed to get hwcomposer display configurations"; return; } int32_t attr_values[5]; uint32_t attributes[] = { HWC_DISPLAY_WIDTH, HWC_DISPLAY_HEIGHT, HWC_DISPLAY_DPI_X, HWC_DISPLAY_DPI_Y, HWC_DISPLAY_VSYNC_PERIOD , HWC_DISPLAY_NO_ATTRIBUTE }; device->getDisplayAttributes(device, 0, configs[0], attributes, attr_values); QSize pixel(attr_values[0], attr_values[1]); if (pixel.isEmpty()) { return; } m_pixelSize = pixel; if (attr_values[2] != 0 && attr_values[3] != 0) { static const qreal factor = 25.4; auto physicalSize = QSizeF(qreal(pixel.width() * 1000) / qreal(attr_values[2]) * factor, qreal(pixel.height() * 1000) / qreal(attr_values[3]) * factor); setRawPhysicalSize(physicalSize.toSize()); } else { // couldn't read physical size, assume 96 dpi setRawPhysicalSize(pixel / 3.8); } OutputDeviceInterface::Mode mode; mode.id = 0; mode.size = pixel; mode.flags = OutputDeviceInterface::ModeFlag::Current | OutputDeviceInterface::ModeFlag::Preferred; mode.refreshRate = (attr_values[4] == 0) ? 60000 : 10E11/attr_values[4]; initWaylandOutputDevice(QString(), QString(), QByteArray(), {mode}); setInternal(true); setEnabled(true); setDpmsSupported(true); const auto outputGroup = kwinApp()->config()->group("HWComposerOutputs").group("0"); setScale(outputGroup.readEntry("Scale", 1)); setWaylandMode(m_pixelSize, mode.refreshRate); } HwcomposerOutput::~HwcomposerOutput() { hwc_close_1(m_device); } QSize HwcomposerOutput::pixelSize() const { return m_pixelSize; } bool HwcomposerOutput::isValid() const { return m_pixelSize.isValid(); } void HwcomposerOutput::updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) { emit dpmsModeRequested(mode); } } diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.h b/plugins/platforms/hwcomposer/hwcomposer_backend.h index 35fbfb7af..cadaff268 100644 --- a/plugins/platforms/hwcomposer/hwcomposer_backend.h +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.h @@ -1,167 +1,167 @@ /******************************************************************** 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 3 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_HWCOMPOSER_BACKEND_H #define KWIN_HWCOMPOSER_BACKEND_H #include "platform.h" -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include "input.h" #include #include #include #include // libhybris #include #include // needed as hwcomposer_window.h includes EGL which on non-arm includes Xlib #include typedef struct hwc_display_contents_1 hwc_display_contents_1_t; typedef struct hwc_layer_1 hwc_layer_1_t; typedef struct hwc_composer_device_1 hwc_composer_device_1_t; struct light_device_t; class HWComposerNativeWindowBuffer; namespace KWin { class HwcomposerWindow; class BacklightInputEventFilter; -class HwcomposerOutput : public AbstractOutput +class HwcomposerOutput : public AbstractWaylandOutput { Q_OBJECT public: HwcomposerOutput(hwc_composer_device_1_t *device); ~HwcomposerOutput() override; bool isValid() const; QSize pixelSize() const override; void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) override; Q_SIGNALS: void dpmsModeRequested(KWayland::Server::OutputInterface::DpmsMode mode); private: QSize m_pixelSize; hwc_composer_device_1_t *m_device; }; class HwcomposerBackend : public Platform { Q_OBJECT Q_INTERFACES(KWin::Platform) Q_PLUGIN_METADATA(IID "org.kde.kwin.Platform" FILE "hwcomposer.json") public: explicit HwcomposerBackend(QObject *parent = nullptr); virtual ~HwcomposerBackend(); void init() override; Screens *createScreens(QObject *parent = nullptr) override; OpenGLBackend *createOpenGLBackend() override; Outputs outputs() const override; Outputs enabledOutputs() const override; QSize size() const; QSize screenSize() const override; int scale() const; HwcomposerWindow *createSurface(); hwc_composer_device_1_t *device() const { return m_device; } void enableVSync(bool enable); void waitVSync(); void wakeVSync(); bool isBacklightOff() const { return m_outputBlank; } QVector supportedCompositors() const override { return QVector{OpenGLCompositing}; } Q_SIGNALS: void outputBlankChanged(); private Q_SLOTS: void toggleBlankOutput(); void screenBrightnessChanged(int brightness) { m_oldScreenBrightness = brightness; } private: void initLights(); void toggleScreenBrightness(); hwc_composer_device_1_t *m_device = nullptr; light_device_t *m_lights = nullptr; bool m_outputBlank = true; int m_vsyncInterval = 16; uint32_t m_hwcVersion; int m_oldScreenBrightness = 0x7f; bool m_hasVsync = false; QMutex m_vsyncMutex; QWaitCondition m_vsyncWaitCondition; QScopedPointer m_filter; QScopedPointer m_output; }; class HwcomposerWindow : public HWComposerNativeWindow { public: virtual ~HwcomposerWindow(); void present(HWComposerNativeWindowBuffer *buffer); private: friend HwcomposerBackend; HwcomposerWindow(HwcomposerBackend *backend); HwcomposerBackend *m_backend; hwc_display_contents_1_t **m_list; }; class BacklightInputEventFilter : public InputEventFilter { public: BacklightInputEventFilter(HwcomposerBackend *backend); virtual ~BacklightInputEventFilter(); bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override; bool wheelEvent(QWheelEvent *event) override; bool keyEvent(QKeyEvent *event) override; bool touchDown(quint32 id, const QPointF &pos, quint32 time) override; bool touchMotion(quint32 id, const QPointF &pos, quint32 time) override; bool touchUp(quint32 id, quint32 time) override; private: void toggleBacklight(); HwcomposerBackend *m_backend; QElapsedTimer m_doubleTapTimer; QVector m_touchPoints; bool m_secondTap = false; }; } #endif diff --git a/plugins/platforms/virtual/virtual_output.cpp b/plugins/platforms/virtual/virtual_output.cpp index 05ba33660..28ede1c27 100644 --- a/plugins/platforms/virtual/virtual_output.cpp +++ b/plugins/platforms/virtual/virtual_output.cpp @@ -1,49 +1,49 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2018 Roman Gilg 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 "virtual_output.h" namespace KWin { VirtualOutput::VirtualOutput(QObject *parent) - : AbstractOutput() + : AbstractWaylandOutput() { Q_UNUSED(parent); setScale(1.); } VirtualOutput::~VirtualOutput() { } QSize VirtualOutput::pixelSize() const { return m_pixelSize; } void VirtualOutput::setGeometry(const QRect &geo) { m_pixelSize = geo.size(); setRawPhysicalSize(m_pixelSize); setGlobalPos(geo.topLeft()); } } diff --git a/plugins/platforms/virtual/virtual_output.h b/plugins/platforms/virtual/virtual_output.h index 450a86869..4569c7dd0 100644 --- a/plugins/platforms/virtual/virtual_output.h +++ b/plugins/platforms/virtual/virtual_output.h @@ -1,64 +1,64 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2018 Roman Gilg 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_VIRTUAL_OUTPUT_H #define KWIN_VIRTUAL_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include #include namespace KWin { class VirtualBackend; -class VirtualOutput : public AbstractOutput +class VirtualOutput : public AbstractWaylandOutput { Q_OBJECT public: VirtualOutput(QObject *parent = nullptr); virtual ~VirtualOutput(); QSize pixelSize() const override; void setGeometry(const QRect &geo); int getGammaRampSize() const override { return m_gammaSize; } bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) override { Q_UNUSED(gamma); return m_gammaResult; } private: Q_DISABLE_COPY(VirtualOutput); friend class VirtualBackend; QSize m_pixelSize; int m_gammaSize = 200; bool m_gammaResult = true; }; } #endif diff --git a/plugins/platforms/wayland/wayland_output.cpp b/plugins/platforms/wayland/wayland_output.cpp index 828f5d622..07aca3912 100644 --- a/plugins/platforms/wayland/wayland_output.cpp +++ b/plugins/platforms/wayland/wayland_output.cpp @@ -1,185 +1,185 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg 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 "wayland_output.h" #include "wayland_backend.h" #include "wayland_server.h" #include #include #include #include #include namespace KWin { namespace Wayland { using namespace KWayland::Client; WaylandOutput::WaylandOutput(Surface *surface, QObject *parent) - : AbstractOutput(parent), + : AbstractWaylandOutput(parent), m_surface(surface) { connect(surface, &Surface::frameRendered, [this] { m_rendered = true; emit frameRendered(); }); } WaylandOutput::~WaylandOutput() { m_surface->destroy(); delete m_surface; } QSize WaylandOutput::pixelSize() const { return m_pixelSize; } void WaylandOutput::setGeometry(const QPoint &logicalPosition, const QSize &pixelSize) { m_pixelSize = pixelSize; setRawPhysicalSize(pixelSize); setGlobalPos(logicalPosition); } ShellOutput::ShellOutput(Surface *surface, Shell *shell, WaylandBackend *backend) : WaylandOutput(surface, backend) { auto shellSurface = shell->createSurface(surface, this); shellSurface->setToplevel(); } ShellOutput::~ShellOutput() { m_shellSurface->destroy(); delete m_shellSurface; } XdgShellOutput::XdgShellOutput(Surface *surface, XdgShell *xdgShell, WaylandBackend *backend, int number) : WaylandOutput(surface, backend) , m_backend(backend) , m_number(number) { m_xdgShellSurface = xdgShell->createSurface(surface, this); updateWindowTitle(); connect(m_xdgShellSurface, &XdgShellSurface::configureRequested, this, &XdgShellOutput::handleConfigure); connect(m_xdgShellSurface, &XdgShellSurface::closeRequested, qApp, &QCoreApplication::quit); connect(backend, &WaylandBackend::pointerLockSupportedChanged, this, &XdgShellOutput::updateWindowTitle); connect(backend, &WaylandBackend::pointerLockChanged, this, [this](bool locked) { if (locked) { if (!m_hasPointerLock) { // some other output has locked the pointer // this surface can stop trying to lock the pointer lockPointer(nullptr, false); // set it true for the other surface m_hasPointerLock = true; } } else { // just try unlocking lockPointer(nullptr, false); } updateWindowTitle(); }); } XdgShellOutput::~XdgShellOutput() { m_xdgShellSurface->destroy(); delete m_xdgShellSurface; } void XdgShellOutput::handleConfigure(const QSize &size, XdgShellSurface::States states, quint32 serial) { Q_UNUSED(states); if (size.width() == 0 || size.height() == 0) { return; } setGeometry(geometry().topLeft(), size); m_xdgShellSurface->ackConfigure(serial); emit sizeChanged(size); } void XdgShellOutput::updateWindowTitle() { QString grab; if (m_hasPointerLock) { grab = i18n("Press right control to ungrab pointer"); } else if (m_backend->pointerConstraints()) { grab = i18n("Press right control key to grab pointer"); } const QString title = i18nc("Title of nested KWin Wayland with Wayland socket identifier as argument", "KDE Wayland Compositor #%1 (%2)", m_number, waylandServer()->display()->socketName()); if (grab.isEmpty()) { m_xdgShellSurface->setTitle(title); } else { m_xdgShellSurface->setTitle(title + QStringLiteral(" — ") + grab); } } void XdgShellOutput::lockPointer(Pointer *pointer, bool lock) { if (!lock) { const bool surfaceWasLocked = m_pointerLock && m_hasPointerLock; delete m_pointerLock; m_pointerLock = nullptr; m_hasPointerLock = false; if (surfaceWasLocked) { emit m_backend->pointerLockChanged(false); } return; } Q_ASSERT(!m_pointerLock); m_pointerLock = m_backend->pointerConstraints()->lockPointer(surface(), pointer, nullptr, PointerConstraints::LifeTime::OneShot, this); if (!m_pointerLock->isValid()) { delete m_pointerLock; m_pointerLock = nullptr; return; } connect(m_pointerLock, &LockedPointer::locked, this, [this] { m_hasPointerLock = true; emit m_backend->pointerLockChanged(true); } ); connect(m_pointerLock, &LockedPointer::unlocked, this, [this] { delete m_pointerLock; m_pointerLock = nullptr; m_hasPointerLock = false; emit m_backend->pointerLockChanged(false); } ); } } } diff --git a/plugins/platforms/wayland/wayland_output.h b/plugins/platforms/wayland/wayland_output.h index 5dfbf3f38..09e749ecb 100644 --- a/plugins/platforms/wayland/wayland_output.h +++ b/plugins/platforms/wayland/wayland_output.h @@ -1,130 +1,130 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg 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_WAYLAND_OUTPUT_H #define KWIN_WAYLAND_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include #include namespace KWayland { namespace Client { class Surface; class Shell; class ShellSurface; class Pointer; class LockedPointer; } } namespace KWin { namespace Wayland { class WaylandBackend; -class WaylandOutput : public AbstractOutput +class WaylandOutput : public AbstractWaylandOutput { Q_OBJECT public: explicit WaylandOutput(KWayland::Client::Surface *surface, QObject *parent = nullptr); ~WaylandOutput(); virtual void lockPointer(KWayland::Client::Pointer *pointer, bool lock) { Q_UNUSED(pointer) Q_UNUSED(lock) } virtual bool pointerIsLocked() { return false; } QSize pixelSize() const override; /** * @brief defines the geometry of the output * @param logicalPosition top left position of the output in compositor space * @param pixelSize output size as seen from the outside */ void setGeometry(const QPoint &logicalPosition, const QSize &pixelSize); KWayland::Client::Surface* surface() const { return m_surface; } bool rendered() const { return m_rendered; } void resetRendered() { m_rendered = false; } Q_SIGNALS: void sizeChanged(const QSize &size); void frameRendered(); private: KWayland::Client::Surface *m_surface; QSize m_pixelSize; bool m_rendered = false; }; class ShellOutput : public WaylandOutput { public: ShellOutput(KWayland::Client::Surface *surface, KWayland::Client::Shell *shell, WaylandBackend *backend); ~ShellOutput(); private: KWayland::Client::ShellSurface *m_shellSurface = nullptr; }; class XdgShellOutput : public WaylandOutput { public: XdgShellOutput(KWayland::Client::Surface *surface, KWayland::Client::XdgShell *xdgShell, WaylandBackend *backend, int number); ~XdgShellOutput(); void lockPointer(KWayland::Client::Pointer *pointer, bool lock) override; private: void handleConfigure(const QSize &size, KWayland::Client::XdgShellSurface::States states, quint32 serial); void updateWindowTitle(); KWayland::Client::XdgShellSurface *m_xdgShellSurface = nullptr; WaylandBackend *m_backend; int m_number; KWayland::Client::LockedPointer *m_pointerLock = nullptr; bool m_hasPointerLock = false; }; } } #endif diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt b/plugins/platforms/x11/standalone/CMakeLists.txt index 266ce176b..80c139b77 100644 --- a/plugins/platforms/x11/standalone/CMakeLists.txt +++ b/plugins/platforms/x11/standalone/CMakeLists.txt @@ -1,44 +1,45 @@ set(X11PLATFORM_SOURCES edge.cpp logging.cpp x11cursor.cpp x11_platform.cpp + x11_output.cpp screens_xrandr.cpp windowselector.cpp overlaywindow_x11.cpp screenedges_filter.cpp non_composited_outline.cpp x11_decoration_renderer.cpp xfixes_cursor_event_filter.cpp effects_x11.cpp effects_mouse_interception_x11_filter.cpp sync_filter.cpp ) if(X11_Xinput_FOUND) set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} xinputintegration.cpp) endif() if(HAVE_EPOXY_GLX) set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} glxbackend.cpp glx_context_attribute_builder.cpp) endif() include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl) add_library(KWinX11Platform MODULE ${X11PLATFORM_SOURCES}) set_target_properties(KWinX11Platform PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/org.kde.kwin.platforms/") target_link_libraries(KWinX11Platform eglx11common kwin kwinxrenderutils SceneOpenGLBackend Qt5::X11Extras XCB::CURSOR KF5::Crash) if(X11_Xinput_FOUND) target_link_libraries(KWinX11Platform ${X11_Xinput_LIB}) endif() if(HAVE_DL_LIBRARY) target_link_libraries(KWinX11Platform ${DL_LIBRARY}) endif() install( TARGETS KWinX11Platform DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kwin.platforms/ ) diff --git a/plugins/platforms/x11/standalone/screens_xrandr.cpp b/plugins/platforms/x11/standalone/screens_xrandr.cpp index 4f552834c..aec04c92f 100644 --- a/plugins/platforms/x11/standalone/screens_xrandr.cpp +++ b/plugins/platforms/x11/standalone/screens_xrandr.cpp @@ -1,230 +1,100 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "screens_xrandr.h" +#include "x11_platform.h" + #ifndef KWIN_UNIT_TEST #include "composite.h" #include "options.h" #include "workspace.h" #endif #include "xcbutils.h" namespace KWin { -XRandRScreens::XRandRScreens(QObject *parent) - : Screens(parent) +XRandRScreens::XRandRScreens(X11StandalonePlatform *backend, QObject *parent) + : OutputScreens(backend, parent) , X11EventFilter(Xcb::Extensions::self()->randrNotifyEvent()) + , m_backend(backend) { } XRandRScreens::~XRandRScreens() = default; -template -void XRandRScreens::update() -{ - auto fallback = [this]() { - m_geometries << QRect(); - m_refreshRates << -1.0f; - m_names << "Xinerama"; - setCount(1); - }; - m_geometries.clear(); - m_names.clear(); - if (!Xcb::Extensions::self()->isRandrAvailable()) { - fallback(); - return; - } - T resources(rootWindow()); - if (resources.isNull()) { - fallback(); - return; - } - xcb_randr_crtc_t *crtcs = resources.crtcs(); - xcb_randr_mode_info_t *modes = resources.modes(); - - QVector infos(resources->num_crtcs); - for (int i = 0; i < resources->num_crtcs; ++i) { - infos[i] = Xcb::RandR::CrtcInfo(crtcs[i], resources->config_timestamp); - } - - for (int i = 0; i < resources->num_crtcs; ++i) { - Xcb::RandR::CrtcInfo info(infos.at(i)); - - xcb_randr_output_t *outputs = info.outputs(); - QVector outputInfos(outputs ? resources->num_outputs : 0); - if (outputs) { - for (int i = 0; i < resources->num_outputs; ++i) { - outputInfos[i] = Xcb::RandR::OutputInfo(outputs[i], resources->config_timestamp); - } - } - - float refreshRate = -1.0f; - for (int j = 0; j < resources->num_modes; ++j) { - if (info->mode == modes[j].id) { - if (modes[j].htotal != 0 && modes[j].vtotal != 0) { // BUG 313996 - // refresh rate calculation - WTF was wikipedia 1998 when I needed it? - int dotclock = modes[j].dot_clock, - vtotal = modes[j].vtotal; - if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE) - dotclock *= 2; - if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN) - vtotal *= 2; - refreshRate = dotclock/float(modes[j].htotal*vtotal); - } - break; // found mode - } - } - - const QRect geo = info.rect(); - if (geo.isValid()) { - m_geometries << geo; - m_refreshRates << refreshRate; - QString name; - for (int j = 0; j < info->num_outputs; ++j) { - Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j)); - if (crtcs[i] == outputInfo->crtc) { - name = outputInfo.name(); - break; - } - } - m_names << name; - } - } - if (m_geometries.isEmpty()) { - fallback(); - return; - } - - setCount(m_geometries.count()); -} - - void XRandRScreens::init() { KWin::Screens::init(); // we need to call ScreenResources at least once to be able to use current - update(); + m_backend->initOutputs(); + setCount(m_backend->outputs().count()); emit changed(); } -QRect XRandRScreens::geometry(int screen) const -{ - if (screen >= m_geometries.size() || screen < 0) { - return QRect(); - } - return m_geometries.at(screen).isValid() ? m_geometries.at(screen) : - QRect(QPoint(0, 0), displaySize()); // xinerama, lacks RandR -} - -QString XRandRScreens::name(int screen) const -{ - if (screen >= m_names.size() || screen < 0) { - return QString(); - } - return m_names.at(screen); -} - -int XRandRScreens::number(const QPoint &pos) const -{ - int bestScreen = 0; - int minDistance = INT_MAX; - for (int i = 0; i < m_geometries.size(); ++i) { - const QRect &geo = m_geometries.at(i); - if (geo.contains(pos)) { - return i; - } - int distance = QPoint(geo.topLeft() - pos).manhattanLength(); - distance = qMin(distance, QPoint(geo.topRight() - pos).manhattanLength()); - distance = qMin(distance, QPoint(geo.bottomRight() - pos).manhattanLength()); - distance = qMin(distance, QPoint(geo.bottomLeft() - pos).manhattanLength()); - if (distance < minDistance) { - minDistance = distance; - bestScreen = i; - } - } - return bestScreen; -} - -float XRandRScreens::refreshRate(int screen) const -{ - if (screen >= m_refreshRates.size() || screen < 0) { - return -1.0f; - } - return m_refreshRates.at(screen); -} - -QSize XRandRScreens::size(int screen) const -{ - const QRect geo = geometry(screen); - if (!geo.isValid()) { - return QSize(); - } - return geo.size(); -} - void XRandRScreens::updateCount() { - update(); + m_backend->updateOutputs(); + setCount(m_backend->outputs().count()); } bool XRandRScreens::event(xcb_generic_event_t *event) { Q_ASSERT((event->response_type & ~0x80) == Xcb::Extensions::self()->randrNotifyEvent()); // let's try to gather a few XRandR events, unlikely that there is just one startChangedTimer(); // update default screen auto *xrrEvent = reinterpret_cast(event); xcb_screen_t *screen = defaultScreen(); if (xrrEvent->rotation & (XCB_RANDR_ROTATION_ROTATE_90 | XCB_RANDR_ROTATION_ROTATE_270)) { screen->width_in_pixels = xrrEvent->height; screen->height_in_pixels = xrrEvent->width; screen->width_in_millimeters = xrrEvent->mheight; screen->height_in_millimeters = xrrEvent->mwidth; } else { screen->width_in_pixels = xrrEvent->width; screen->height_in_pixels = xrrEvent->height; screen->width_in_millimeters = xrrEvent->mwidth; screen->height_in_millimeters = xrrEvent->mheight; } #ifndef KWIN_UNIT_TEST if (workspace()->compositing()) { // desktopResized() should take care of when the size or // shape of the desktop has changed, but we also want to // catch refresh rate changes if (Compositor::self()->xrrRefreshRate() != Options::currentRefreshRate()) Compositor::self()->setCompositeResetTimer(0); } #endif return false; } QSize XRandRScreens::displaySize() const { xcb_screen_t *screen = defaultScreen(); if (!screen) { return Screens::size(); } return QSize(screen->width_in_pixels, screen->height_in_pixels); } } // namespace diff --git a/plugins/platforms/x11/standalone/screens_xrandr.h b/plugins/platforms/x11/standalone/screens_xrandr.h index 5a7a74879..e0f9b6a08 100644 --- a/plugins/platforms/x11/standalone/screens_xrandr.h +++ b/plugins/platforms/x11/standalone/screens_xrandr.h @@ -1,61 +1,51 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_SCREENS_XRANDR_H #define KWIN_SCREENS_XRANDR_H // kwin -#include "screens.h" +#include "outputscreens.h" #include "x11eventfilter.h" -// Qt -#include namespace KWin { +class X11StandalonePlatform; -class XRandRScreens : public Screens, public X11EventFilter +class XRandRScreens : public OutputScreens, public X11EventFilter { Q_OBJECT public: - XRandRScreens(QObject *parent); + XRandRScreens(X11StandalonePlatform *backend, QObject *parent = nullptr); virtual ~XRandRScreens(); void init() override; - QRect geometry(int screen) const override; - QString name(int screen) const override; - int number(const QPoint& pos) const override; - float refreshRate(int screen) const override; - QSize size(int screen) const override; + QSize displaySize() const override; using QObject::event; bool event(xcb_generic_event_t *event) override; -protected Q_SLOTS: +private: void updateCount() override; -private: - template - void update(); - QVector m_geometries; - QVector m_refreshRates; - QVector m_names; + X11StandalonePlatform *m_backend; }; } // namespace #endif diff --git a/plugins/platforms/virtual/virtual_output.cpp b/plugins/platforms/x11/standalone/x11_output.cpp similarity index 57% copy from plugins/platforms/virtual/virtual_output.cpp copy to plugins/platforms/x11/standalone/x11_output.cpp index 05ba33660..9a1e30a08 100644 --- a/plugins/platforms/virtual/virtual_output.cpp +++ b/plugins/platforms/x11/standalone/x11_output.cpp @@ -1,49 +1,64 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 2018 Roman Gilg +Copyright 2019 Roman Gilg 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 "virtual_output.h" +#include "x11_output.h" +#include "screens.h" namespace KWin { -VirtualOutput::VirtualOutput(QObject *parent) - : AbstractOutput() +X11Output::X11Output(QObject *parent) + : AbstractOutput(parent) { - Q_UNUSED(parent); +} + +QString X11Output::name() const +{ + return m_name; +} - setScale(1.); +void X11Output::setName(QString set) +{ + m_name = set; +} + +QRect X11Output::geometry() const +{ + if (m_geometry.isValid()) { + return m_geometry; + } + return QRect(QPoint(0, 0), Screens::self()->displaySize()); // xinerama, lacks RandR } -VirtualOutput::~VirtualOutput() +void X11Output::setGeometry(QRect set) { + m_geometry = set; } -QSize VirtualOutput::pixelSize() const +int X11Output::refreshRate() const { - return m_pixelSize; + return m_refreshRate; } -void VirtualOutput::setGeometry(const QRect &geo) +void X11Output::setRefreshRate(int set) { - m_pixelSize = geo.size(); - setRawPhysicalSize(m_pixelSize); - setGlobalPos(geo.topLeft()); + m_refreshRate = set; } } diff --git a/plugins/platforms/virtual/virtual_output.h b/plugins/platforms/x11/standalone/x11_output.h similarity index 56% copy from plugins/platforms/virtual/virtual_output.h copy to plugins/platforms/x11/standalone/x11_output.h index 450a86869..d5f094058 100644 --- a/plugins/platforms/virtual/virtual_output.h +++ b/plugins/platforms/x11/standalone/x11_output.h @@ -1,64 +1,64 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 2018 Roman Gilg +Copyright 2019 Roman Gilg 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_VIRTUAL_OUTPUT_H -#define KWIN_VIRTUAL_OUTPUT_H +#ifndef KWIN_X11_OUTPUT_H +#define KWIN_X11_OUTPUT_H #include "abstract_output.h" +#include #include #include namespace KWin { -class VirtualBackend; -class VirtualOutput : public AbstractOutput +/** + * X11 output representation + **/ +class KWIN_EXPORT X11Output : public AbstractOutput { Q_OBJECT - public: - VirtualOutput(QObject *parent = nullptr); - virtual ~VirtualOutput(); - - QSize pixelSize() const override; - - void setGeometry(const QRect &geo); - - int getGammaRampSize() const override { - return m_gammaSize; - } - bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) override { - Q_UNUSED(gamma); - return m_gammaResult; - } + explicit X11Output(QObject *parent = nullptr); + virtual ~X11Output() = default; + + QString name() const override; + void setName(QString set); + /** + * The geometry of this output in global compositor co-ordinates (i.e scaled) + **/ + QRect geometry() const override; + void setGeometry(QRect set); + + /** + * Current refresh rate in 1/ms. + **/ + int refreshRate() const override; + void setRefreshRate(int set); private: - Q_DISABLE_COPY(VirtualOutput); - friend class VirtualBackend; - - QSize m_pixelSize; - - int m_gammaSize = 200; - bool m_gammaResult = true; + QString m_name; + QRect m_geometry; + int m_refreshRate; }; } #endif diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp index e7b5fba2f..215af1ca2 100644 --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -1,442 +1,549 @@ /******************************************************************** 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 "x11cursor.h" #include "edge.h" #include "sync_filter.h" #include "windowselector.h" #include #include #if HAVE_EPOXY_GLX #include "glxbackend.h" #endif #if HAVE_X11_XINPUT #include "xinputintegration.h" #endif #include "abstract_client.h" #include "effects_x11.h" #include "eglonxbackend.h" #include "keyboard_input.h" #include "logging.h" #include "screens_xrandr.h" #include "screenedges_filter.h" #include "options.h" #include "overlaywindow_x11.h" #include "non_composited_outline.h" #include "workspace.h" #include "x11_decoration_renderer.h" +#include "x11_output.h" +#include "xcbutils.h" #include #include #include #include #include #include #include namespace KWin { X11StandalonePlatform::X11StandalonePlatform(QObject *parent) : Platform(parent) , m_x11Display(QX11Info::display()) { #if HAVE_X11_XINPUT if (!qEnvironmentVariableIsSet("KWIN_NO_XI2")) { m_xinputIntegration = new XInputIntegration(m_x11Display, this); m_xinputIntegration->init(); if (!m_xinputIntegration->hasXinput()) { delete m_xinputIntegration; m_xinputIntegration = nullptr; } else { connect(kwinApp(), &Application::workspaceCreated, m_xinputIntegration, &XInputIntegration::startListening); } } #endif connect(kwinApp(), &Application::workspaceCreated, this, [this] { if (Xcb::Extensions::self()->isSyncAvailable()) { m_syncFilter = std::make_unique(); } } ); } X11StandalonePlatform::~X11StandalonePlatform() { if (m_openGLFreezeProtectionThread) { m_openGLFreezeProtectionThread->quit(); m_openGLFreezeProtectionThread->wait(); delete m_openGLFreezeProtectionThread; } if (isReady()) { XRenderUtils::cleanup(); } } void X11StandalonePlatform::init() { if (!QX11Info::isPlatformX11()) { emit initFailed(); return; } XRenderUtils::init(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); setReady(true); emit screensQueried(); } Screens *X11StandalonePlatform::createScreens(QObject *parent) { - return new XRandRScreens(parent); + return new XRandRScreens(this, parent); } OpenGLBackend *X11StandalonePlatform::createOpenGLBackend() { switch (options->glPlatformInterface()) { #if HAVE_EPOXY_GLX case GlxPlatformInterface: if (hasGlx()) { return new GlxBackend(m_x11Display); } else { qCWarning(KWIN_X11STANDALONE) << "Glx not available, trying EGL instead."; // no break, needs fall-through Q_FALLTHROUGH(); } #endif case EglPlatformInterface: return new EglOnXBackend(m_x11Display); default: // no backend available return nullptr; } } Edge *X11StandalonePlatform::createScreenEdge(ScreenEdges *edges) { if (m_screenEdgesFilter.isNull()) { m_screenEdgesFilter.reset(new ScreenEdgesFilter); } return new WindowBasedEdge(edges); } void X11StandalonePlatform::createPlatformCursor(QObject *parent) { auto c = new X11Cursor(parent, m_xinputIntegration != nullptr); #if HAVE_X11_XINPUT if (m_xinputIntegration) { m_xinputIntegration->setCursor(c); // we know we have xkb already auto xkb = input()->keyboard()->xkb(); xkb->setConfig(kwinApp()->kxkbConfig()); xkb->reconfigure(); } #endif } 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(); } void X11StandalonePlatform::createOpenGLSafePoint(OpenGLSafePoint safePoint) { const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); auto group = KConfigGroup(kwinApp()->config(), "Compositing"); switch (safePoint) { case OpenGLSafePoint::PreInit: group.writeEntry(unsafeKey, true); group.sync(); // Deliberately continue with PreFrame Q_FALLTHROUGH(); case OpenGLSafePoint::PreFrame: if (m_openGLFreezeProtectionThread == nullptr) { Q_ASSERT(m_openGLFreezeProtection == nullptr); m_openGLFreezeProtectionThread = new QThread(this); m_openGLFreezeProtectionThread->setObjectName("FreezeDetector"); m_openGLFreezeProtectionThread->start(); m_openGLFreezeProtection = new QTimer; m_openGLFreezeProtection->setInterval(15000); m_openGLFreezeProtection->setSingleShot(true); m_openGLFreezeProtection->start(); const QString configName = kwinApp()->config()->name(); m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread); connect(m_openGLFreezeProtection, &QTimer::timeout, m_openGLFreezeProtection, [configName] { const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing"); group.writeEntry(unsafeKey, true); group.sync(); KCrash::setDrKonqiEnabled(false); qFatal("Freeze in OpenGL initialization detected"); }, Qt::DirectConnection); } else { Q_ASSERT(m_openGLFreezeProtection); QMetaObject::invokeMethod(m_openGLFreezeProtection, "start", Qt::QueuedConnection); } break; case OpenGLSafePoint::PostInit: group.writeEntry(unsafeKey, false); group.sync(); // Deliberately continue with PostFrame Q_FALLTHROUGH(); case OpenGLSafePoint::PostFrame: QMetaObject::invokeMethod(m_openGLFreezeProtection, "stop", Qt::QueuedConnection); break; case OpenGLSafePoint::PostLastGuardedFrame: m_openGLFreezeProtection->deleteLater(); m_openGLFreezeProtection = nullptr; m_openGLFreezeProtectionThread->quit(); m_openGLFreezeProtectionThread->wait(); delete m_openGLFreezeProtectionThread; m_openGLFreezeProtectionThread = nullptr; break; } } PlatformCursorImage X11StandalonePlatform::cursorImage() const { auto c = kwinApp()->x11Connection(); QScopedPointer cursor( xcb_xfixes_get_cursor_image_reply(c, xcb_xfixes_get_cursor_image_unchecked(c), nullptr)); if (cursor.isNull()) { return PlatformCursorImage(); } QImage qcursorimg((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height, QImage::Format_ARGB32_Premultiplied); // deep copy of image as the data is going to be freed return PlatformCursorImage(qcursorimg.copy(), QPoint(cursor->xhot, cursor->yhot)); } void X11StandalonePlatform::doHideCursor() { xcb_xfixes_hide_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); } void X11StandalonePlatform::doShowCursor() { xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); } void X11StandalonePlatform::startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName) { if (m_windowSelector.isNull()) { m_windowSelector.reset(new WindowSelector); } m_windowSelector->start(callback, cursorName); } void X11StandalonePlatform::startInteractivePositionSelection(std::function callback) { if (m_windowSelector.isNull()) { m_windowSelector.reset(new WindowSelector); } m_windowSelector->start(callback); } void X11StandalonePlatform::setupActionForGlobalAccel(QAction *action) { connect(action, &QAction::triggered, kwinApp(), [action] { QVariant timestamp = action->property("org.kde.kglobalaccel.activationTimestamp"); bool ok = false; const quint32 t = timestamp.toULongLong(&ok); if (ok) { kwinApp()->setX11Time(t); } }); } OverlayWindow *X11StandalonePlatform::createOverlayWindow() { return new OverlayWindowX11(); } /* Updates xTime(). This used to simply fetch current timestamp from the server, but that can cause xTime() to be newer than timestamp of events that are still in our events queue, thus e.g. making XSetInputFocus() caused by such event to be ignored. Therefore events queue is searched for first event with timestamp, and extra PropertyNotify is generated in order to make sure such event is found. */ void X11StandalonePlatform::updateXTime() { // NOTE: QX11Info::getTimestamp does not yet search the event queue as the old // solution did. This means there might be regressions currently. See the // documentation above on how it should be done properly. kwinApp()->setX11Time(QX11Info::getTimestamp(), Application::TimestampUpdate::Always); } OutlineVisual *X11StandalonePlatform::createOutline(Outline *outline) { // first try composited Outline auto ret = Platform::createOutline(outline); if (!ret) { ret = new NonCompositedOutlineVisual(outline); } return ret; } Decoration::Renderer *X11StandalonePlatform::createDecorationRenderer(Decoration::DecoratedClientImpl *client) { auto renderer = Platform::createDecorationRenderer(client); if (!renderer) { renderer = new Decoration::X11Renderer(client); } return renderer; } void X11StandalonePlatform::invertScreen() { using namespace Xcb::RandR; bool succeeded = false; if (Xcb::Extensions::self()->isRandrAvailable()) { const auto active_client = workspace()->activeClient(); ScreenResources res((active_client && active_client->window() != XCB_WINDOW_NONE) ? active_client->window() : rootWindow()); if (!res.isNull()) { for (int j = 0; j < res->num_crtcs; ++j) { auto crtc = res.crtcs()[j]; CrtcGamma gamma(crtc); if (gamma.isNull()) { continue; } if (gamma->size) { qCDebug(KWIN_CORE) << "inverting screen using xcb_randr_set_crtc_gamma"; const int half = gamma->size / 2 + 1; uint16_t *red = gamma.red(); uint16_t *green = gamma.green(); uint16_t *blue = gamma.blue(); for (int i = 0; i < half; ++i) { auto invert = [&gamma, i](uint16_t *ramp) { qSwap(ramp[i], ramp[gamma->size - 1 - i]); }; invert(red); invert(green); invert(blue); } xcb_randr_set_crtc_gamma(connection(), crtc, gamma->size, red, green, blue); succeeded = true; } } } } if (!succeeded) { Platform::invertScreen(); } } void X11StandalonePlatform::createEffectsHandler(Compositor *compositor, Scene *scene) { new EffectsHandlerImplX11(compositor, scene); } QVector X11StandalonePlatform::supportedCompositors() const { QVector compositors; #if HAVE_EPOXY_GLX compositors << OpenGLCompositing; #endif #ifdef KWIN_HAVE_XRENDER_COMPOSITING compositors << XRenderCompositing; #endif compositors << NoCompositing; return compositors; } +void X11StandalonePlatform::initOutputs() +{ + doUpdateOutputs(); +} + +void X11StandalonePlatform::updateOutputs() +{ + doUpdateOutputs(); +} + +template +void X11StandalonePlatform::doUpdateOutputs() +{ + auto fallback = [this]() { + auto *o = new X11Output(this); + o->setRefreshRate(-1.0f); + o->setName(QStringLiteral("Xinerama")); + m_outputs << o; + }; + + // TODO: instead of resetting all outputs, check if new output is added/removed + // or still available and leave still available outputs in m_outputs + // untouched (like in DRM backend) + qDeleteAll(m_outputs); + m_outputs.clear(); + + if (!Xcb::Extensions::self()->isRandrAvailable()) { + fallback(); + return; + } + T resources(rootWindow()); + if (resources.isNull()) { + fallback(); + return; + } + xcb_randr_crtc_t *crtcs = resources.crtcs(); + xcb_randr_mode_info_t *modes = resources.modes(); + + QVector infos(resources->num_crtcs); + for (int i = 0; i < resources->num_crtcs; ++i) { + infos[i] = Xcb::RandR::CrtcInfo(crtcs[i], resources->config_timestamp); + } + + for (int i = 0; i < resources->num_crtcs; ++i) { + Xcb::RandR::CrtcInfo info(infos.at(i)); + + xcb_randr_output_t *outputs = info.outputs(); + QVector outputInfos(outputs ? resources->num_outputs : 0); + if (outputs) { + for (int i = 0; i < resources->num_outputs; ++i) { + outputInfos[i] = Xcb::RandR::OutputInfo(outputs[i], resources->config_timestamp); + } + } + + float refreshRate = -1.0f; + for (int j = 0; j < resources->num_modes; ++j) { + if (info->mode == modes[j].id) { + if (modes[j].htotal != 0 && modes[j].vtotal != 0) { // BUG 313996 + // refresh rate calculation - WTF was wikipedia 1998 when I needed it? + int dotclock = modes[j].dot_clock, + vtotal = modes[j].vtotal; + if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE) + dotclock *= 2; + if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN) + vtotal *= 2; + refreshRate = dotclock/float(modes[j].htotal*vtotal); + } + break; // found mode + } + } + + const QRect geo = info.rect(); + if (geo.isValid()) { + auto *o = new X11Output(this); + o->setGeometry(geo); + o->setRefreshRate(refreshRate); + + QString name; + for (int j = 0; j < info->num_outputs; ++j) { + Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j)); + if (crtcs[i] == outputInfo->crtc) { + name = outputInfo.name(); + break; + } + } + o->setName(name); + m_outputs << o; + } + } + + if (m_outputs.isEmpty()) { + fallback(); + } +} + +Outputs X11StandalonePlatform::outputs() const +{ + return m_outputs; +} + +Outputs X11StandalonePlatform::enabledOutputs() const +{ + return m_outputs; +} + } diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h index 732177537..44e4e4ad0 100644 --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -1,102 +1,113 @@ /******************************************************************** 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_X11_PLATFORM_H #define KWIN_X11_PLATFORM_H #include "platform.h" #include #include #include namespace KWin { class SyncFilter; class XInputIntegration; class WindowSelector; class X11EventFilter; +class X11Output; 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; void createPlatformCursor(QObject *parent = nullptr) override; bool requiresCompositing() const override; bool compositingPossible() const override; QString compositingNotPossibleReason() const override; bool openGLCompositingIsBroken() const override; void createOpenGLSafePoint(OpenGLSafePoint safePoint) override; void startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName = QByteArray()) override; void startInteractivePositionSelection(std::function callback) override; PlatformCursorImage cursorImage() const override; void setupActionForGlobalAccel(QAction *action) override; OverlayWindow *createOverlayWindow() override; void updateXTime() override; OutlineVisual *createOutline(Outline *outline) override; Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *client) override; void invertScreen() override; void createEffectsHandler(Compositor *compositor, Scene *scene) override; QVector supportedCompositors() const override; + void initOutputs(); + void updateOutputs(); + + Outputs outputs() const override; + Outputs enabledOutputs() const override; + protected: void doHideCursor() override; void doShowCursor() 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(); + template + void doUpdateOutputs(); + XInputIntegration *m_xinputIntegration = nullptr; QThread *m_openGLFreezeProtectionThread = nullptr; QTimer *m_openGLFreezeProtection = nullptr; Display *m_x11Display; QScopedPointer m_windowSelector; QScopedPointer m_screenEdgesFilter; std::unique_ptr m_syncFilter; + QVector m_outputs; }; } #endif diff --git a/plugins/platforms/x11/windowed/x11windowed_output.cpp b/plugins/platforms/x11/windowed/x11windowed_output.cpp index cbcbd65a5..9889d3276 100644 --- a/plugins/platforms/x11/windowed/x11windowed_output.cpp +++ b/plugins/platforms/x11/windowed/x11windowed_output.cpp @@ -1,156 +1,156 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg 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 "x11windowed_output.h" #include "x11windowed_backend.h" #include #if HAVE_X11_XINPUT #include #endif #include namespace KWin { X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend) - : AbstractOutput(backend) + : AbstractWaylandOutput(backend) , m_backend(backend) { m_window = xcb_generate_id(m_backend->connection()); } X11WindowedOutput::~X11WindowedOutput() { xcb_unmap_window(m_backend->connection(), m_window); xcb_destroy_window(m_backend->connection(), m_window); delete m_winInfo; xcb_flush(m_backend->connection()); } void X11WindowedOutput::init() { uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; const uint32_t values[] = { m_backend->screen()->black_pixel, XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_EXPOSURE }; xcb_create_window(m_backend->connection(), XCB_COPY_FROM_PARENT, m_window, m_backend->screen()->root, 0, 0, pixelSize().width(), pixelSize().height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values); // select xinput 2 events initXInputForWindow(); m_winInfo = new NETWinInfo(m_backend->connection(), m_window, m_backend->screen()->root, NET::WMWindowType, NET::Properties2()); m_winInfo->setWindowType(NET::Normal); m_winInfo->setPid(QCoreApplication::applicationPid()); QIcon windowIcon = QIcon::fromTheme(QStringLiteral("kwin")); auto addIcon = [&windowIcon, this] (const QSize &size) { if (windowIcon.actualSize(size) != size) { return; } NETIcon icon; icon.data = windowIcon.pixmap(size).toImage().bits(); icon.size.width = size.width(); icon.size.height = size.height(); m_winInfo->setIcon(icon, false); }; addIcon(QSize(16, 16)); addIcon(QSize(32, 32)); addIcon(QSize(48, 48)); xcb_map_window(m_backend->connection(), m_window); } void X11WindowedOutput::initXInputForWindow() { if (!m_backend->hasXInput()) { return; } #if HAVE_X11_XINPUT XIEventMask evmasks[1]; unsigned char mask1[XIMaskLen(XI_LASTEVENT)]; memset(mask1, 0, sizeof(mask1)); XISetMask(mask1, XI_TouchBegin); XISetMask(mask1, XI_TouchUpdate); XISetMask(mask1, XI_TouchOwnership); XISetMask(mask1, XI_TouchEnd); evmasks[0].deviceid = XIAllMasterDevices; evmasks[0].mask_len = sizeof(mask1); evmasks[0].mask = mask1; XISelectEvents(m_backend->display(), m_window, evmasks, 1); #else Q_UNUSED(window) #endif } QSize X11WindowedOutput::pixelSize() const { return m_pixelSize; } void X11WindowedOutput::setGeometry(const QPoint &logicalPosition, const QSize &pixelSize) { m_pixelSize = pixelSize; // Physicial size must be adjusted, such that QPA calculates correct sizes of // internal elements. setRawPhysicalSize(pixelSize / 96.0 * 25.4 / scale()); setGlobalPos(logicalPosition); } void X11WindowedOutput::setWindowTitle(const QString &title) { m_winInfo->setName(title.toUtf8().constData()); } QPoint X11WindowedOutput::internalPosition() const { return geometry().topLeft(); } void X11WindowedOutput::setHostPosition(const QPoint &pos) { m_hostPosition = pos; } } diff --git a/plugins/platforms/x11/windowed/x11windowed_output.h b/plugins/platforms/x11/windowed/x11windowed_output.h index 06390c2b5..893b1882d 100644 --- a/plugins/platforms/x11/windowed/x11windowed_output.h +++ b/plugins/platforms/x11/windowed/x11windowed_output.h @@ -1,85 +1,85 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright 2019 Roman Gilg 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_X11WINDOWED_OUTPUT_H #define KWIN_X11WINDOWED_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include #include #include #include #include class NETWinInfo; namespace KWin { class X11WindowedBackend; /** * Wayland outputs in a nested X11 setup **/ -class KWIN_EXPORT X11WindowedOutput : public AbstractOutput +class KWIN_EXPORT X11WindowedOutput : public AbstractWaylandOutput { Q_OBJECT public: explicit X11WindowedOutput(X11WindowedBackend *backend); ~X11WindowedOutput() override; void init(); xcb_window_t window() const { return m_window; } QPoint internalPosition() const; QPoint hostPosition() const { return m_hostPosition; } void setHostPosition(const QPoint &pos); void setWindowTitle(const QString &title); QSize pixelSize() const override; /** * @brief defines the geometry of the output * @param logicalPosition top left position of the output in compositor space * @param pixelSize output size as seen from the outside */ void setGeometry(const QPoint &logicalPosition, const QSize &pixelSize); private: void initXInputForWindow(); xcb_window_t m_window = XCB_WINDOW_NONE; NETWinInfo *m_winInfo = nullptr; QPoint m_hostPosition; QSize m_pixelSize; X11WindowedBackend *m_backend; }; } #endif