diff --git a/CMakeLists.txt b/CMakeLists.txt index db07ca1f4..d70dbc50b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,717 +1,723 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(KWIN) set(PROJECT_VERSION "5.15.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.11.0") set(KF5_MIN_VERSION "5.56.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 "http://github.com/anholt/libepoxy" TYPE REQUIRED PURPOSE "OpenGL dispatch library" ) set(HAVE_DL_LIBRARY FALSE) if(epoxy_HAS_GLX) find_library(DL_LIBRARY dl) if(DL_LIBRARY) set(HAVE_DL_LIBRARY TRUE) endif() endif() find_package(Wayland 1.2 REQUIRED COMPONENTS Cursor OPTIONAL_COMPONENTS Egl) set_package_properties(Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" ) add_feature_info("Wayland::EGL" Wayland_Egl_FOUND "Enable building of Wayland backend and QPA with EGL support.") set(HAVE_WAYLAND_EGL FALSE) if(Wayland_Egl_FOUND) set(HAVE_WAYLAND_EGL TRUE) endif() find_package(XKB 0.7.0) set_package_properties(XKB PROPERTIES TYPE REQUIRED PURPOSE "Required for building KWin with Wayland support" ) find_package(Libinput 1.9) set_package_properties(Libinput PROPERTIES TYPE REQUIRED PURPOSE "Required for input handling on Wayland.") find_package(UDev) set_package_properties(UDev PROPERTIES URL "http://www.freedesktop.org/software/systemd/libudev/" DESCRIPTION "Linux device library." TYPE REQUIRED PURPOSE "Required for input handling on Wayland." ) find_package(Libdrm 2.4.62) set_package_properties(Libdrm PROPERTIES TYPE OPTIONAL PURPOSE "Required for drm output on Wayland.") set(HAVE_DRM FALSE) if(Libdrm_FOUND) set(HAVE_DRM TRUE) endif() find_package(gbm) set_package_properties(gbm PROPERTIES TYPE OPTIONAL PURPOSE "Required for egl output of drm backend.") set(HAVE_GBM FALSE) if(HAVE_DRM AND gbm_FOUND) set(HAVE_GBM TRUE) endif() find_package(libhybris) set_package_properties(libhybris PROPERTIES TYPE OPTIONAL PURPOSE "Required for libhybris backend") set(HAVE_LIBHYBRIS ${libhybris_FOUND}) find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE REQUIRED ) add_feature_info("XInput" X11_Xinput_FOUND "Required for poll-free mouse cursor updates") set(HAVE_X11_XINPUT ${X11_Xinput_FOUND}) # All the required XCB components find_package(XCB 1.10 REQUIRED COMPONENTS XCB XFIXES DAMAGE COMPOSITE SHAPE SYNC RENDER RANDR KEYSYMS IMAGE SHM GLX CURSOR ICCCM ) set_package_properties(XCB PROPERTIES TYPE REQUIRED) # and the optional XCB dependencies if (XCB_ICCCM_VERSION VERSION_LESS "0.4") set(XCB_ICCCM_FOUND FALSE) endif() add_feature_info("XCB-ICCCM" XCB_ICCCM_FOUND "Required for building test applications for KWin") find_package(X11_XCB) set_package_properties(X11_XCB PROPERTIES PURPOSE "Required for building X11 windowed backend of kwin_wayland" TYPE OPTIONAL) # dependencies for QPA plugin find_package(Qt5FontDatabaseSupport REQUIRED) find_package(Qt5ThemeSupport REQUIRED) find_package(Qt5EventDispatcherSupport REQUIRED) find_package(Freetype REQUIRED) set_package_properties(Freetype PROPERTIES DESCRIPTION "A font rendering engine" URL "http://www.freetype.org" TYPE REQUIRED PURPOSE "Needed for KWin's QPA plugin." ) find_package(Fontconfig REQUIRED) set_package_properties(Fontconfig PROPERTIES TYPE REQUIRED PURPOSE "Needed for KWin's QPA plugin." ) find_package(Xwayland) set_package_properties(Xwayland PROPERTIES URL "http://x.org" DESCRIPTION "Xwayland X server" TYPE RUNTIME PURPOSE "Needed for running kwin_wayland" ) find_package(Libcap) set_package_properties(Libcap PROPERTIES TYPE OPTIONAL PURPOSE "Needed for running kwin_wayland with real-time scheduling policy" ) set(HAVE_LIBCAP ${Libcap_FOUND}) include(ECMQMLModules) ecm_find_qmlmodule(QtQuick 2.3) ecm_find_qmlmodule(QtQuick.Controls 1.2) ecm_find_qmlmodule(QtQuick.Layouts 1.3) ecm_find_qmlmodule(QtQuick.VirtualKeyboard 2.1) ecm_find_qmlmodule(QtQuick.Window 2.1) ecm_find_qmlmodule(QtMultimedia 5.0) ecm_find_qmlmodule(org.kde.kquickcontrolsaddons 2.0) ecm_find_qmlmodule(org.kde.plasma.core 2.0) ecm_find_qmlmodule(org.kde.plasma.components 2.0) ########### configure tests ############### include(CMakeDependentOption) option(KWIN_BUILD_DECORATIONS "Enable building of KWin decorations." ON) option(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON) option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON) option(KWIN_BUILD_XRENDER_COMPOSITING "Enable building of KWin with XRender Compositing support" ON) cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "KF5Activities_FOUND" OFF) # Binary name of KWin set(KWIN_NAME "kwin") set(KWIN_INTERNAL_NAME_X11 "kwin_x11") set(KWIN_INTERNAL_NAME_WAYLAND "kwin_wayland") set(KWIN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # KWIN_HAVE_XRENDER_COMPOSITING - whether XRender-based compositing support is available: may be disabled if( KWIN_BUILD_XRENDER_COMPOSITING ) set( KWIN_HAVE_XRENDER_COMPOSITING 1 ) endif() include_directories(${XKB_INCLUDE_DIR}) include_directories(${epoxy_INCLUDE_DIR}) set(HAVE_EPOXY_GLX ${epoxy_HAS_GLX}) # for things that are also used by kwin libraries configure_file(libkwineffects/kwinconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects/kwinconfig.h ) # for kwin internal things set(HAVE_X11_XCB ${X11_XCB_FOUND}) include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckSymbolExists) check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(malloc.h HAVE_MALLOC_H) check_include_file("sys/prctl.h" HAVE_SYS_PRCTL_H) check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE) check_symbol_exists(PR_SET_PDEATHSIG "sys/prctl.h" HAVE_PR_SET_PDEATHSIG) check_include_file("sys/procctl.h" HAVE_SYS_PROCCTL_H) check_symbol_exists(PROC_TRACE_CTL "sys/procctl.h" HAVE_PROC_TRACE_CTL) if (HAVE_PR_SET_DUMPABLE OR HAVE_PROC_TRACE_CTL) set(CAN_DISABLE_PTRACE TRUE) endif() add_feature_info("prctl/procctl tracing control" CAN_DISABLE_PTRACE "Required for disallowing ptrace on kwin_wayland process") check_include_file("sys/sysmacros.h" HAVE_SYS_SYSMACROS_H) configure_file(config-kwin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwin.h ) check_include_file("linux/vt.h" HAVE_LINUX_VT_H) add_feature_info("linux/vt.h" HAVE_LINUX_VT_H "Required for virtual terminal support under wayland") check_include_file("linux/fb.h" HAVE_LINUX_FB_H) add_feature_info("linux/fb.h" HAVE_LINUX_FB_H "Required for the fbdev backend") check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK) add_feature_info("SCHED_RESET_ON_FORK" HAVE_SCHED_RESET_ON_FORK "Required for running kwin_wayland with real-time scheduling") ########### global ############### set(kwin_effects_dbus_xml ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.kwin.Effects.xml) include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/libkwineffects ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libkwineffects ${CMAKE_CURRENT_SOURCE_DIR}/effects ${CMAKE_CURRENT_SOURCE_DIR}/tabbox ${CMAKE_CURRENT_SOURCE_DIR}/platformsupport ) add_subdirectory( libkwineffects ) if(KWIN_BUILD_KCMS) add_subdirectory( kcmkwin ) endif() add_subdirectory( data ) add_subdirectory( effects ) add_subdirectory( scripts ) add_subdirectory( tabbox ) add_subdirectory(scripting) add_subdirectory(helpers) ########### next target ############### set(kwin_KDEINIT_SRCS workspace.cpp dbusinterface.cpp virtualdesktopsdbustypes.cpp abstract_client.cpp client.cpp client_machine.cpp cursor.cpp debug_console.cpp tabgroup.cpp focuschain.cpp globalshortcuts.cpp input.cpp input_event.cpp input_event_spy.cpp keyboard_input.cpp keyboard_layout.cpp keyboard_layout_switching.cpp keyboard_repeat.cpp pointer_input.cpp touch_input.cpp netinfo.cpp placement.cpp atoms.cpp utils.cpp layers.cpp main.cpp options.cpp outline.cpp events.cpp killwindow.cpp geometrytip.cpp screens.cpp outputscreens.cpp shadow.cpp sm.cpp group.cpp manage.cpp overlaywindow.cpp activation.cpp useractions.cpp geometry.cpp rules.cpp composite.cpp toplevel.cpp unmanaged.cpp scene.cpp screenlockerwatcher.cpp thumbnailitem.cpp deleted.cpp effects.cpp effectloader.cpp virtualdesktops.cpp xcbutils.cpp x11eventfilter.cpp logind.cpp onscreennotification.cpp osd.cpp screenedge.cpp scripting/scripting.cpp scripting/workspace_wrapper.cpp scripting/meta.cpp scripting/scriptedeffect.cpp scripting/scriptingutils.cpp scripting/timer.cpp scripting/scripting_model.cpp scripting/dbuscall.cpp scripting/screenedgeitem.cpp scripting/scripting_logging.cpp decorations/decoratedclient.cpp decorations/decorationbridge.cpp decorations/decorationpalette.cpp decorations/settings.cpp decorations/decorationrenderer.cpp decorations/decorations_logging.cpp platform.cpp abstract_output.cpp shell_client.cpp wayland_server.cpp wayland_cursor_theme.cpp virtualkeyboard.cpp virtualkeyboard_dbus.cpp appmenu.cpp modifier_only_shortcuts.cpp xkb.cpp gestures.cpp popup_input_filter.cpp colorcorrection/manager.cpp colorcorrection/colorcorrectdbusinterface.cpp colorcorrection/suncalc.cpp abstract_opengl_context_attribute_builder.cpp egl_context_attribute_builder.cpp was_user_interaction_x11_filter.cpp moving_client_x11_filter.cpp window_property_notify_x11_filter.cpp rootinfo_filter.cpp orientation_sensor.cpp idle_inhibition.cpp libinput/context.cpp libinput/connection.cpp libinput/device.cpp libinput/events.cpp libinput/libinput_logging.cpp udev.cpp touch_hide_cursor_spy.cpp ) include(ECMQtDeclareLoggingCategory) ecm_qt_declare_logging_category(kwin_KDEINIT_SRCS HEADER colorcorrect_logging.h IDENTIFIER KWIN_COLORCORRECTION CATEGORY_NAME kwin_colorcorrection DEFAULT_SEVERITY Critical ) if(KWIN_BUILD_TABBOX) include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set( kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} tabbox/tabbox.cpp tabbox/clientmodel.cpp tabbox/desktopchain.cpp tabbox/desktopmodel.cpp tabbox/switcheritem.cpp tabbox/tabboxconfig.cpp tabbox/tabboxhandler.cpp tabbox/tabbox_logging.cpp tabbox/x11_filter.cpp ) endif() if(KWIN_BUILD_ACTIVITIES) set( kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} activities.cpp ) endif() if (HAVE_LINUX_VT_H) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} virtual_terminal.cpp ) endif() kconfig_add_kcfg_files(kwin_KDEINIT_SRCS settings.kcfgc) kconfig_add_kcfg_files(kwin_KDEINIT_SRCS colorcorrection/colorcorrect_settings.kcfgc) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml dbusinterface.h KWin::DBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.Compositing.xml dbusinterface.h KWin::CompositorDBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.ColorCorrect.xml colorcorrection/colorcorrectdbusinterface.h KWin::ColorCorrect::ColorCorrectDBusInterface ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl ) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.OrientationSensor.xml orientation_sensor.h KWin::OrientationSensor) qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface ) qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.ScreenSaver.xml screenlocker_interface) qt5_add_dbus_interface( kwin_KDEINIT_SRCS org.kde.kappmenu.xml appmenu_interface ) ki18n_wrap_ui(kwin_KDEINIT_SRCS debug_console.ui shortcutdialog.ui ) ########### target link libraries ############### set(kwin_OWN_LIBS kwineffects kwin4_effect_builtins ) set(kwin_QT_LIBS Qt5::Concurrent Qt5::DBus Qt5::Quick Qt5::Sensors Qt5::Script ) set(kwin_KDE_LIBS KF5::ConfigCore KF5::CoreAddons KF5::ConfigWidgets KF5::GlobalAccel KF5::GlobalAccelPrivate KF5::I18n KF5::Notifications KF5::Package KF5::Plasma KF5::WindowSystem KF5::QuickAddons KDecoration2::KDecoration KDecoration2::KDecoration2Private PW::KScreenLocker ) set(kwin_XLIB_LIBS ${X11_X11_LIB} ${X11_ICE_LIB} ${X11_SM_LIB} ) set(kwin_XCB_LIBS XCB::XCB XCB::XFIXES XCB::DAMAGE XCB::COMPOSITE XCB::SHAPE XCB::SYNC XCB::RENDER XCB::RANDR XCB::KEYSYMS XCB::SHM XCB::GLX XCB::ICCCM ) set(kwin_WAYLAND_LIBS XKB::XKB KF5::WaylandClient KF5::WaylandServer Wayland::Cursor ${CMAKE_THREAD_LIBS_INIT} ) if(KWIN_BUILD_ACTIVITIES) set(kwin_KDE_LIBS ${kwin_KDE_LIBS} KF5::Activities) endif() set(kwinLibs ${kwin_OWN_LIBS} ${kwin_QT_LIBS} ${kwin_KDE_LIBS} ${kwin_XLIB_LIBS} ${kwin_XCB_LIBS} ${kwin_WAYLAND_LIBS} ${UDEV_LIBS} Libinput::Libinput ) add_library(kwin SHARED ${kwin_KDEINIT_SRCS}) set_target_properties(kwin PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) target_link_libraries(kwin ${kwinLibs}) generate_export_header(kwin EXPORT_FILE_NAME kwin_export.h) target_link_libraries(kwin kwinglutils ${epoxy_LIBRARY}) kf5_add_kdeinit_executable(kwin_x11 main_x11.cpp) target_link_libraries(kdeinit_kwin_x11 kwin KF5::Crash Qt5::X11Extras) install(TARGETS kwin ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP ) install(TARGETS kdeinit_kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(TARGETS kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) -add_executable(kwin_wayland tabletmodemanager.cpp main_wayland.cpp) +set(kwin_WAYLAND_SRCS + tabletmodemanager.cpp + main_wayland.cpp + xwl/xwayland.cpp + ) + +add_executable(kwin_wayland ${kwin_WAYLAND_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) add_subdirectory(packageplugins) if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() if (KF5DocTools_FOUND) add_subdirectory(doc) endif() add_subdirectory(kconf_update) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) include(CMakePackageConfigHelpers) set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KWinDBusInterface") configure_package_config_file(KWinDBusInterfaceConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/KWinDBusInterfaceConfig.cmake" PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KWinDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) diff --git a/main_wayland.cpp b/main_wayland.cpp index ef3a653b3..f5b6e7874 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -1,831 +1,659 @@ /******************************************************************** 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 "main_wayland.h" #include "composite.h" #include "virtualkeyboard.h" #include "workspace.h" #include // kwin #include "platform.h" #include "effects.h" #include "tabletmodemanager.h" #include "wayland_server.h" -#include "xcbutils.h" +#include "xwl/xwayland.h" // KWayland #include #include // KDE #include #include #include #include #include // Qt #include -#include #include -#include -#include #include -#include #include -#include #include -#include #include #include // system -#ifdef HAVE_UNISTD_H -#include -#endif // HAVE_UNISTD_H - #if HAVE_SYS_PRCTL_H #include #endif #if HAVE_SYS_PROCCTL_H -#include #include #endif #if HAVE_LIBCAP #include #endif #include #include #include namespace KWin { static void sighandler(int) { QApplication::exit(); } void disableDrKonqi() { KCrash::setDrKonqiEnabled(false); } // run immediately, before Q_CORE_STARTUP functions // that would enable drkonqi Q_CONSTRUCTOR_FUNCTION(disableDrKonqi) -static void readDisplay(int pipe); - enum class RealTimeFlags { DontReset, ResetOnFork }; namespace { void gainRealTime(RealTimeFlags flags = RealTimeFlags::DontReset) { #if HAVE_SCHED_RESET_ON_FORK const int minPriority = sched_get_priority_min(SCHED_RR); struct sched_param sp; sp.sched_priority = minPriority; int policy = SCHED_RR; if (flags == RealTimeFlags::ResetOnFork) { policy |= SCHED_RESET_ON_FORK; } sched_setscheduler(0, policy, &sp); #else Q_UNUSED(flags); #endif } } //************************************ // ApplicationWayland //************************************ ApplicationWayland::ApplicationWayland(int &argc, char **argv) : Application(OperationModeWaylandOnly, argc, argv) { } ApplicationWayland::~ApplicationWayland() { setTerminating(); if (!waylandServer()) { return; } if (kwinApp()->platform()) { kwinApp()->platform()->setOutputsEnabled(false); } // need to unload all effects prior to destroying X connection as they might do X calls if (effects) { static_cast(effects)->unloadAllEffects(); } destroyWorkspace(); waylandServer()->dispatch(); - disconnect(m_xwaylandFailConnection); - if (x11Connection()) { - Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); - destroyAtoms(); - emit x11ConnectionAboutToBeDestroyed(); - xcb_disconnect(x11Connection()); - setX11Connection(nullptr); - } - if (m_xwaylandProcess) { - m_xwaylandProcess->terminate(); - while (m_xwaylandProcess->state() != QProcess::NotRunning) { - processEvents(QEventLoop::WaitForMoreEvents); - } - waylandServer()->destroyXWaylandConnection(); - } + if (QStyle *s = style()) { s->unpolish(this); } + // kill Xwayland before terminating its connection + delete m_xwayland; + m_xwayland = nullptr; waylandServer()->terminateClientConnections(); destroyCompositor(); } void ApplicationWayland::performStartup() { if (m_startXWayland) { setOperationMode(OperationModeXwayland); } // first load options - done internally by a different thread createOptions(); waylandServer()->createInternalConnection(); // try creating the Wayland Backend createInput(); // now libinput thread has been created, adjust scheduler to not leak into other processes gainRealTime(RealTimeFlags::ResetOnFork); VirtualKeyboard::create(this); createBackend(); TabletModeManager::create(this); } void ApplicationWayland::createBackend() { connect(platform(), &Platform::screensQueried, this, &ApplicationWayland::continueStartupWithScreens); connect(platform(), &Platform::initFailed, this, [] () { std::cerr << "FATAL ERROR: backend failed to initialize, exiting now" << std::endl; QCoreApplication::exit(1); } ); platform()->init(); } void ApplicationWayland::continueStartupWithScreens() { disconnect(kwinApp()->platform(), &Platform::screensQueried, this, &ApplicationWayland::continueStartupWithScreens); createScreens(); if (operationMode() == OperationModeWaylandOnly) { createCompositor(); connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithSceen); return; } createCompositor(); - connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::startXwaylandServer); + connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland); } void ApplicationWayland::continueStartupWithSceen() { disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithSceen); startSession(); createWorkspace(); notifyKSplash(); } -void ApplicationWayland::continueStartupWithX() +void ApplicationWayland::continueStartupWithXwayland() { - createX11Connection(); - xcb_connection_t *c = x11Connection(); - if (!c) { - // about to quit - return; - } - QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(c), QSocketNotifier::Read, this); - auto processXcbEvents = [this, c] { - while (auto event = xcb_poll_for_event(c)) { - long result = 0; - QThread::currentThread()->eventDispatcher()->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result); - free(event); - } - xcb_flush(c); - }; - connect(notifier, &QSocketNotifier::activated, this, processXcbEvents); - connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents); - connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); - - // create selection owner for WM_S0 - magic X display number expected by XWayland - KSelectionOwner owner("WM_S0", c, x11RootWindow()); - owner.claim(true); - - createAtoms(); - - setupEventFilters(); - - // Check whether another windowmanager is running - const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT}; - ScopedCPointer redirectCheck(xcb_request_check(connection(), - xcb_change_window_attributes_checked(connection(), - rootWindow(), - XCB_CW_EVENT_MASK, - maskValues))); - if (!redirectCheck.isNull()) { - fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr); - ::exit(1); - } - - m_environment.insert(QStringLiteral("DISPLAY"), QString::fromUtf8(qgetenv("DISPLAY"))); - - startSession(); - createWorkspace(); - - Xcb::sync(); // Trigger possible errors, there's still a chance to abort - - notifyKSplash(); + disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland); + + m_xwayland = new Xwl::Xwayland(this); + connect(m_xwayland, &Xwl::Xwayland::criticalError, this, [](int code) { + // we currently exit on Xwayland errors always directly + // TODO: restart Xwayland + std::cerr << "Xwayland had a critical error. Going to exit now." << std::endl; + exit(code); + }); + m_xwayland->init(); } void ApplicationWayland::startSession() { if (!m_inputMethodServerToStart.isEmpty()) { int socket = dup(waylandServer()->createInputMethodConnection()); if (socket >= 0) { QProcessEnvironment environment = m_environment; environment.insert(QStringLiteral("WAYLAND_SOCKET"), QByteArray::number(socket)); environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); environment.remove("DISPLAY"); environment.remove("WAYLAND_DISPLAY"); QProcess *p = new Process(this); p->setProcessChannelMode(QProcess::ForwardedErrorChannel); auto finishedSignal = static_cast(&QProcess::finished); connect(p, finishedSignal, this, [this, p] { if (waylandServer()) { waylandServer()->destroyInputMethodConnection(); } p->deleteLater(); } ); p->setProcessEnvironment(environment); p->start(m_inputMethodServerToStart); p->waitForStarted(); } } // start session if (!m_sessionArgument.isEmpty()) { QProcess *p = new Process(this); p->setProcessChannelMode(QProcess::ForwardedErrorChannel); p->setProcessEnvironment(m_environment); auto finishedSignal = static_cast(&QProcess::finished); connect(p, finishedSignal, this, &ApplicationWayland::quit); p->start(m_sessionArgument); } // start the applications passed to us as command line arguments if (!m_applicationsToStart.isEmpty()) { for (const QString &application: m_applicationsToStart) { // note: this will kill the started process when we exit // this is going to happen anyway as we are the wayland and X server the app connects to QProcess *p = new Process(this); p->setProcessChannelMode(QProcess::ForwardedErrorChannel); p->setProcessEnvironment(m_environment); p->start(application); } } } -void ApplicationWayland::createX11Connection() -{ - int screenNumber = 0; - xcb_connection_t *c = nullptr; - if (m_xcbConnectionFd == -1) { - c = xcb_connect(nullptr, &screenNumber); - } else { - c = xcb_connect_to_fd(m_xcbConnectionFd, nullptr); - } - if (int error = xcb_connection_has_error(c)) { - std::cerr << "FATAL ERROR: Creating connection to XServer failed: " << error << std::endl; - exit(1); - return; - } - setX11Connection(c); - // we don't support X11 multi-head in Wayland - setX11ScreenNumber(screenNumber); - setX11RootWindow(defaultScreen()->root); -} - -void ApplicationWayland::startXwaylandServer() -{ - disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::startXwaylandServer); - int pipeFds[2]; - if (pipe(pipeFds) != 0) { - std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl; - exit(1); - return; - } - int sx[2]; - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { - std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; - exit(1); - return; - } - int fd = dup(sx[1]); - if (fd < 0) { - std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; - exit(20); - return; - } - - const int waylandSocket = waylandServer()->createXWaylandConnection(); - if (waylandSocket == -1) { - std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; - exit(1); - return; - } - const int wlfd = dup(waylandSocket); - if (wlfd < 0) { - std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; - exit(20); - return; - } - - m_xcbConnectionFd = sx[0]; - - m_xwaylandProcess = new Process(kwinApp()); - m_xwaylandProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel); - m_xwaylandProcess->setProgram(QStringLiteral("Xwayland")); - QProcessEnvironment env = m_environment; - env.insert("WAYLAND_SOCKET", QByteArray::number(wlfd)); - env.insert("EGL_PLATFORM", QByteArrayLiteral("DRM")); - m_xwaylandProcess->setProcessEnvironment(env); - m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"), - QString::number(pipeFds[1]), - QStringLiteral("-rootless"), - QStringLiteral("-wm"), - QString::number(fd)}); - m_xwaylandFailConnection = connect(m_xwaylandProcess, static_cast(&QProcess::error), this, - [] (QProcess::ProcessError error) { - if (error == QProcess::FailedToStart) { - std::cerr << "FATAL ERROR: failed to start Xwayland" << std::endl; - } else { - std::cerr << "FATAL ERROR: Xwayland failed, going to exit now" << std::endl; - } - exit(1); - } - ); - const int xDisplayPipe = pipeFds[0]; - connect(m_xwaylandProcess, &QProcess::started, this, - [this, xDisplayPipe] { - QFutureWatcher *watcher = new QFutureWatcher(this); - QObject::connect(watcher, &QFutureWatcher::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection); - QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater, Qt::QueuedConnection); - watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe)); - } - ); - m_xwaylandProcess->start(); - close(pipeFds[1]); -} - -static void readDisplay(int pipe) -{ - QFile readPipe; - if (!readPipe.open(pipe, QIODevice::ReadOnly)) { - std::cerr << "FATAL ERROR failed to open pipe to start X Server" << std::endl; - exit(1); - } - QByteArray displayNumber = readPipe.readLine(); - - displayNumber.prepend(QByteArray(":")); - displayNumber.remove(displayNumber.size() -1, 1); - std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; - - setenv("DISPLAY", displayNumber.constData(), true); - - // close our pipe - close(pipe); -} - static const QString s_waylandPlugin = QStringLiteral("KWinWaylandWaylandBackend"); static const QString s_x11Plugin = QStringLiteral("KWinWaylandX11Backend"); static const QString s_fbdevPlugin = QStringLiteral("KWinWaylandFbdevBackend"); #if HAVE_DRM static const QString s_drmPlugin = QStringLiteral("KWinWaylandDrmBackend"); #endif #if HAVE_LIBHYBRIS static const QString s_hwcomposerPlugin = QStringLiteral("KWinWaylandHwcomposerBackend"); #endif static const QString s_virtualPlugin = QStringLiteral("KWinWaylandVirtualBackend"); static QString automaticBackendSelection() { if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY")) { return s_waylandPlugin; } if (qEnvironmentVariableIsSet("DISPLAY")) { return s_x11Plugin; } #if HAVE_LIBHYBRIS if (qEnvironmentVariableIsSet("ANDROID_ROOT")) { return s_hwcomposerPlugin; } #endif #if HAVE_DRM return s_drmPlugin; #endif return s_fbdevPlugin; } static void disablePtrace() { #if HAVE_PR_SET_DUMPABLE // check whether we are running under a debugger const QFileInfo parent(QStringLiteral("/proc/%1/exe").arg(getppid())); if (parent.isSymLink() && (parent.symLinkTarget().endsWith(QLatin1String("/gdb")) || parent.symLinkTarget().endsWith(QLatin1String("/gdbserver")))) { // debugger, don't adjust return; } // disable ptrace in kwin_wayland prctl(PR_SET_DUMPABLE, 0); #endif #if HAVE_PROC_TRACE_CTL // FreeBSD's rudimentary procfs does not support /proc//exe // We could use the P_TRACED flag of the process to find out // if the process is being debugged ond FreeBSD. int mode = PROC_TRACE_CTL_DISABLE; procctl(P_PID, getpid(), PROC_TRACE_CTL, &mode); #endif } static void unsetDumpable(int sig) { #if HAVE_PR_SET_DUMPABLE prctl(PR_SET_DUMPABLE, 1); #endif signal(sig, SIG_IGN); raise(sig); return; } void dropNiceCapability() { #if HAVE_LIBCAP cap_t caps = cap_get_proc(); if (!caps) { return; } cap_value_t capList[] = { CAP_SYS_NICE }; if (cap_set_flag(caps, CAP_PERMITTED, 1, capList, CAP_CLEAR) == -1) { cap_free(caps); return; } if (cap_set_flag(caps, CAP_EFFECTIVE, 1, capList, CAP_CLEAR) == -1) { cap_free(caps); return; } cap_set_proc(caps); cap_free(caps); #endif } } // namespace int main(int argc, char * argv[]) { if (getuid() == 0) { std::cerr << "kwin_wayland does not support running as root." << std::endl; return 1; } KWin::disablePtrace(); KWin::Application::setupMalloc(); KWin::Application::setupLocalizedString(); KWin::gainRealTime(); KWin::dropNiceCapability(); if (signal(SIGTERM, KWin::sighandler) == SIG_IGN) signal(SIGTERM, SIG_IGN); if (signal(SIGINT, KWin::sighandler) == SIG_IGN) signal(SIGINT, SIG_IGN); if (signal(SIGHUP, KWin::sighandler) == SIG_IGN) signal(SIGHUP, SIG_IGN); signal(SIGABRT, KWin::unsetDumpable); signal(SIGSEGV, KWin::unsetDumpable); signal(SIGPIPE, SIG_IGN); // ensure that no thread takes SIGUSR sigset_t userSignals; sigemptyset(&userSignals); sigaddset(&userSignals, SIGUSR1); sigaddset(&userSignals, SIGUSR2); pthread_sigmask(SIG_BLOCK, &userSignals, nullptr); QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); // enforce our internal qpa plugin, unfortunately command line switch has precedence setenv("QT_QPA_PLATFORM", "wayland-org.kde.kwin.qpa", true); qunsetenv("QT_DEVICE_PIXEL_RATIO"); qputenv("QT_IM_MODULE", "qtvirtualkeyboard"); qputenv("QSG_RENDER_LOOP", "basic"); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); KWin::ApplicationWayland a(argc, argv); a.setupTranslator(); // reset QT_QPA_PLATFORM to a sane value for any processes started from KWin setenv("QT_QPA_PLATFORM", "wayland", true); KWin::Application::createAboutData(); KQuickAddons::QtQuickSettings::init(); const auto availablePlugins = KPluginLoader::findPlugins(QStringLiteral("org.kde.kwin.waylandbackends")); auto hasPlugin = [&availablePlugins] (const QString &name) { return std::any_of(availablePlugins.begin(), availablePlugins.end(), [name] (const KPluginMetaData &plugin) { return plugin.pluginId() == name; } ); }; const bool hasSizeOption = hasPlugin(KWin::s_x11Plugin) || hasPlugin(KWin::s_virtualPlugin); const bool hasOutputCountOption = hasPlugin(KWin::s_x11Plugin); const bool hasX11Option = hasPlugin(KWin::s_x11Plugin); const bool hasVirtualOption = hasPlugin(KWin::s_virtualPlugin); const bool hasWaylandOption = hasPlugin(KWin::s_waylandPlugin); const bool hasFramebufferOption = hasPlugin(KWin::s_fbdevPlugin); #if HAVE_DRM const bool hasDrmOption = hasPlugin(KWin::s_drmPlugin); #endif #if HAVE_LIBHYBRIS const bool hasHwcomposerOption = hasPlugin(KWin::s_hwcomposerPlugin); #endif QCommandLineOption xwaylandOption(QStringLiteral("xwayland"), i18n("Start a rootless Xwayland server.")); QCommandLineOption waylandSocketOption(QStringList{QStringLiteral("s"), QStringLiteral("socket")}, i18n("Name of the Wayland socket to listen on. If not set \"wayland-0\" is used."), QStringLiteral("socket")); QCommandLineOption framebufferOption(QStringLiteral("framebuffer"), i18n("Render to framebuffer.")); QCommandLineOption framebufferDeviceOption(QStringLiteral("fb-device"), i18n("The framebuffer device to render to."), QStringLiteral("fbdev")); QCommandLineOption x11DisplayOption(QStringLiteral("x11-display"), i18n("The X11 Display to use in windowed mode on platform X11."), QStringLiteral("display")); QCommandLineOption waylandDisplayOption(QStringLiteral("wayland-display"), i18n("The Wayland Display to use in windowed mode on platform Wayland."), QStringLiteral("display")); QCommandLineOption virtualFbOption(QStringLiteral("virtual"), i18n("Render to a virtual framebuffer.")); QCommandLineOption widthOption(QStringLiteral("width"), i18n("The width for windowed mode. Default width is 1024."), QStringLiteral("width")); widthOption.setDefaultValue(QString::number(1024)); QCommandLineOption heightOption(QStringLiteral("height"), i18n("The height for windowed mode. Default height is 768."), QStringLiteral("height")); heightOption.setDefaultValue(QString::number(768)); QCommandLineOption scaleOption(QStringLiteral("scale"), i18n("The scale for windowed mode. Default value is 1."), QStringLiteral("scale")); scaleOption.setDefaultValue(QString::number(1)); QCommandLineOption outputCountOption(QStringLiteral("output-count"), i18n("The number of windows to open as outputs in windowed mode. Default value is 1"), QStringLiteral("height")); outputCountOption.setDefaultValue(QString::number(1)); QCommandLineParser parser; a.setupCommandLine(&parser); parser.addOption(xwaylandOption); parser.addOption(waylandSocketOption); if (hasX11Option) { parser.addOption(x11DisplayOption); } if (hasWaylandOption) { parser.addOption(waylandDisplayOption); } if (hasFramebufferOption) { parser.addOption(framebufferOption); parser.addOption(framebufferDeviceOption); } if (hasVirtualOption) { parser.addOption(virtualFbOption); } if (hasSizeOption) { parser.addOption(widthOption); parser.addOption(heightOption); parser.addOption(scaleOption); } if (hasOutputCountOption) { parser.addOption(outputCountOption); } #if HAVE_LIBHYBRIS QCommandLineOption hwcomposerOption(QStringLiteral("hwcomposer"), i18n("Use libhybris hwcomposer")); if (hasHwcomposerOption) { parser.addOption(hwcomposerOption); } #endif QCommandLineOption libinputOption(QStringLiteral("libinput"), i18n("Enable libinput support for input events processing. Note: never use in a nested session.")); parser.addOption(libinputOption); #if HAVE_DRM QCommandLineOption drmOption(QStringLiteral("drm"), i18n("Render through drm node.")); if (hasDrmOption) { parser.addOption(drmOption); } #endif QCommandLineOption inputMethodOption(QStringLiteral("inputmethod"), i18n("Input method that KWin starts."), QStringLiteral("path/to/imserver")); parser.addOption(inputMethodOption); QCommandLineOption listBackendsOption(QStringLiteral("list-backends"), i18n("List all available backends and quit.")); parser.addOption(listBackendsOption); QCommandLineOption screenLockerOption(QStringLiteral("lockscreen"), i18n("Starts the session in locked mode.")); parser.addOption(screenLockerOption); QCommandLineOption noScreenLockerOption(QStringLiteral("no-lockscreen"), i18n("Starts the session without lock screen support.")); parser.addOption(noScreenLockerOption); QCommandLineOption noGlobalShortcutsOption(QStringLiteral("no-global-shortcuts"), i18n("Starts the session without global shortcuts support.")); parser.addOption(noGlobalShortcutsOption); QCommandLineOption exitWithSessionOption(QStringLiteral("exit-with-session"), i18n("Exit after the session application, which is started by KWin, closed."), QStringLiteral("/path/to/session")); parser.addOption(exitWithSessionOption); parser.addPositionalArgument(QStringLiteral("applications"), i18n("Applications to start once Wayland and Xwayland server are started"), QStringLiteral("[/path/to/application...]")); parser.process(a); a.processCommandLine(&parser); #ifdef KWIN_BUILD_ACTIVITIES a.setUseKActivities(false); #endif if (parser.isSet(listBackendsOption)) { for (const auto &plugin: availablePlugins) { std::cout << std::setw(40) << std::left << qPrintable(plugin.name()) << qPrintable(plugin.description()) << std::endl; } return 0; } if (parser.isSet(exitWithSessionOption)) { a.setSessionArgument(parser.value(exitWithSessionOption)); } KWin::Application::setUseLibinput(parser.isSet(libinputOption)); QString pluginName; QSize initialWindowSize; QByteArray deviceIdentifier; int outputCount = 1; qreal outputScale = 1; #if HAVE_DRM if (hasDrmOption && parser.isSet(drmOption)) { pluginName = KWin::s_drmPlugin; } #endif if (hasSizeOption) { bool ok = false; const int width = parser.value(widthOption).toInt(&ok); if (!ok) { std::cerr << "FATAL ERROR incorrect value for width" << std::endl; return 1; } const int height = parser.value(heightOption).toInt(&ok); if (!ok) { std::cerr << "FATAL ERROR incorrect value for height" << std::endl; return 1; } const qreal scale = parser.value(scaleOption).toDouble(&ok); if (!ok || scale < 1) { std::cerr << "FATAL ERROR incorrect value for scale" << std::endl; return 1; } outputScale = scale; initialWindowSize = QSize(width, height); } if (hasOutputCountOption) { bool ok = false; const int count = parser.value(outputCountOption).toInt(&ok); if (ok) { outputCount = qMax(1, count); } } if (hasX11Option && parser.isSet(x11DisplayOption)) { deviceIdentifier = parser.value(x11DisplayOption).toUtf8(); pluginName = KWin::s_x11Plugin; } else if (hasWaylandOption && parser.isSet(waylandDisplayOption)) { deviceIdentifier = parser.value(waylandDisplayOption).toUtf8(); pluginName = KWin::s_waylandPlugin; } if (hasFramebufferOption && parser.isSet(framebufferOption)) { pluginName = KWin::s_fbdevPlugin; deviceIdentifier = parser.value(framebufferDeviceOption).toUtf8(); } #if HAVE_LIBHYBRIS if (hasHwcomposerOption && parser.isSet(hwcomposerOption)) { pluginName = KWin::s_hwcomposerPlugin; } #endif if (hasVirtualOption && parser.isSet(virtualFbOption)) { pluginName = KWin::s_virtualPlugin; } if (pluginName.isEmpty()) { std::cerr << "No backend specified through command line argument, trying auto resolution" << std::endl; pluginName = KWin::automaticBackendSelection(); } auto pluginIt = std::find_if(availablePlugins.begin(), availablePlugins.end(), [&pluginName] (const KPluginMetaData &plugin) { return plugin.pluginId() == pluginName; } ); if (pluginIt == availablePlugins.end()) { std::cerr << "FATAL ERROR: could not find a backend" << std::endl; return 1; } // TODO: create backend without having the server running KWin::WaylandServer *server = KWin::WaylandServer::create(&a); KWin::WaylandServer::InitalizationFlags flags; if (parser.isSet(screenLockerOption)) { flags = KWin::WaylandServer::InitalizationFlag::LockScreen; } else if (parser.isSet(noScreenLockerOption)) { flags = KWin::WaylandServer::InitalizationFlag::NoLockScreenIntegration; } if (parser.isSet(noGlobalShortcutsOption)) { flags |= KWin::WaylandServer::InitalizationFlag::NoGlobalShortcuts; } if (!server->init(parser.value(waylandSocketOption).toUtf8(), flags)) { std::cerr << "FATAL ERROR: could not create Wayland server" << std::endl; return 1; } a.initPlatform(*pluginIt); if (!a.platform()) { std::cerr << "FATAL ERROR: could not instantiate a backend" << std::endl; return 1; } if (!deviceIdentifier.isEmpty()) { a.platform()->setDeviceIdentifier(deviceIdentifier); } if (initialWindowSize.isValid()) { a.platform()->setInitialWindowSize(initialWindowSize); } a.platform()->setInitialOutputScale(outputScale); a.platform()->setInitialOutputCount(outputCount); QObject::connect(&a, &KWin::Application::workspaceCreated, server, &KWin::WaylandServer::initWorkspace); environment.insert(QStringLiteral("WAYLAND_DISPLAY"), server->display()->socketName()); a.setProcessStartupEnvironment(environment); a.setStartXwayland(parser.isSet(xwaylandOption)); a.setApplicationsToStart(parser.positionalArguments()); a.setInputMethodServerToStart(parser.value(inputMethodOption)); a.start(); return a.exec(); } diff --git a/main_wayland.h b/main_wayland.h index 31b7ebd55..9e13082ac 100644 --- a/main_wayland.h +++ b/main_wayland.h @@ -1,81 +1,82 @@ /******************************************************************** 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_MAIN_WAYLAND_H #define KWIN_MAIN_WAYLAND_H #include "main.h" #include -class QProcess; - namespace KWin { +namespace Xwl +{ +class Xwayland; +} class ApplicationWayland : public Application { Q_OBJECT public: ApplicationWayland(int &argc, char **argv); virtual ~ApplicationWayland(); void setStartXwayland(bool start) { m_startXWayland = start; } void setApplicationsToStart(const QStringList &applications) { m_applicationsToStart = applications; } void setInputMethodServerToStart(const QString &inputMethodServer) { m_inputMethodServerToStart = inputMethodServer; } void setProcessStartupEnvironment(const QProcessEnvironment &environment) { m_environment = environment; } void setSessionArgument(const QString &session) { m_sessionArgument = session; } QProcessEnvironment processStartupEnvironment() const override { return m_environment; } protected: void performStartup() override; private: + friend class Xwl::Xwayland; + void createBackend(); - void createX11Connection(); void continueStartupWithScreens(); void continueStartupWithSceen(); - void continueStartupWithX(); - void startXwaylandServer(); + void continueStartupWithXwayland(); void startSession(); bool m_startXWayland = false; - int m_xcbConnectionFd = -1; QStringList m_applicationsToStart; QString m_inputMethodServerToStart; - QProcess *m_xwaylandProcess = nullptr; - QMetaObject::Connection m_xwaylandFailConnection; QProcessEnvironment m_environment; QString m_sessionArgument; + + Xwl::Xwayland *m_xwayland = nullptr; }; } #endif diff --git a/xwl/xwayland.cpp b/xwl/xwayland.cpp new file mode 100644 index 000000000..28c704d3b --- /dev/null +++ b/xwl/xwayland.cpp @@ -0,0 +1,245 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2014 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 . +*********************************************************************/ +#include "xwayland.h" +#include "wayland_server.h" +#include "main_wayland.h" +#include "utils.h" + +#include + +#include "xcbutils.h" + +#include +#include +#include +#include +#include +#include +#include + +// system +#ifdef HAVE_UNISTD_H +#include +#endif +#if HAVE_SYS_PROCCTL_H +#include +#endif + +#include +#include + +static void readDisplay(int pipe) +{ + QFile readPipe; + if (!readPipe.open(pipe, QIODevice::ReadOnly)) { + std::cerr << "FATAL ERROR failed to open pipe to start X Server" << std::endl; + exit(1); + } + QByteArray displayNumber = readPipe.readLine(); + + displayNumber.prepend(QByteArray(":")); + displayNumber.remove(displayNumber.size() -1, 1); + std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; + + setenv("DISPLAY", displayNumber.constData(), true); + + // close our pipe + close(pipe); +} + +namespace KWin { +namespace Xwl +{ + +Xwayland::Xwayland(ApplicationWayland *app, QObject *parent) + : QObject(parent), + m_app(app) +{ +} + +Xwayland::~Xwayland() +{ + disconnect(m_xwaylandFailConnection); + if (m_app->x11Connection()) { + Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); + m_app->destroyAtoms(); + Q_EMIT m_app->x11ConnectionAboutToBeDestroyed(); + xcb_disconnect(m_app->x11Connection()); + m_app->setX11Connection(nullptr); + } + + if (m_xwaylandProcess) { + m_xwaylandProcess->terminate(); + while (m_xwaylandProcess->state() != QProcess::NotRunning) { + m_app->processEvents(QEventLoop::WaitForMoreEvents); + } + waylandServer()->destroyXWaylandConnection(); + } +} + +void Xwayland::init() +{ + int pipeFds[2]; + if (pipe(pipeFds) != 0) { + std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl; + Q_EMIT criticalError(1); + return; + } + int sx[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { + std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; + Q_EMIT criticalError(1); + return; + } + int fd = dup(sx[1]); + if (fd < 0) { + std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl; + Q_EMIT criticalError(20); + return; + } + + const int waylandSocket = waylandServer()->createXWaylandConnection(); + if (waylandSocket == -1) { + std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; + Q_EMIT criticalError(1); + return; + } + const int wlfd = dup(waylandSocket); + if (wlfd < 0) { + std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl; + Q_EMIT criticalError(20); + return; + } + + m_xcbConnectionFd = sx[0]; + + m_xwaylandProcess = new Process(this); + m_xwaylandProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel); + m_xwaylandProcess->setProgram(QStringLiteral("Xwayland")); + QProcessEnvironment env = m_app->processStartupEnvironment(); + env.insert("WAYLAND_SOCKET", QByteArray::number(wlfd)); + env.insert("EGL_PLATFORM", QByteArrayLiteral("DRM")); + m_xwaylandProcess->setProcessEnvironment(env); + m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"), + QString::number(pipeFds[1]), + QStringLiteral("-rootless"), + QStringLiteral("-wm"), + QString::number(fd)}); + m_xwaylandFailConnection = connect(m_xwaylandProcess, static_cast(&QProcess::error), this, + [this] (QProcess::ProcessError error) { + if (error == QProcess::FailedToStart) { + std::cerr << "FATAL ERROR: failed to start Xwayland" << std::endl; + } else { + std::cerr << "FATAL ERROR: Xwayland failed, going to exit now" << std::endl; + } + Q_EMIT criticalError(1); + } + ); + const int xDisplayPipe = pipeFds[0]; + connect(m_xwaylandProcess, &QProcess::started, this, + [this, xDisplayPipe] { + QFutureWatcher *watcher = new QFutureWatcher(this); + QObject::connect(watcher, &QFutureWatcher::finished, this, &Xwayland::continueStartupWithX, Qt::QueuedConnection); + QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater, Qt::QueuedConnection); + watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe)); + } + ); + m_xwaylandProcess->start(); + close(pipeFds[1]); +} + +void Xwayland::createX11Connection() +{ + int screenNumber = 0; + xcb_connection_t *c = nullptr; + if (m_xcbConnectionFd == -1) { + c = xcb_connect(nullptr, &screenNumber); + } else { + c = xcb_connect_to_fd(m_xcbConnectionFd, nullptr); + } + if (int error = xcb_connection_has_error(c)) { + std::cerr << "FATAL ERROR: Creating connection to XServer failed: " << error << std::endl; + Q_EMIT criticalError(1); + return; + } + + m_app->setX11Connection(c); + // we don't support X11 multi-head in Wayland + m_app->setX11ScreenNumber(screenNumber); + m_app->setX11RootWindow(defaultScreen()->root); +} + +void Xwayland::continueStartupWithX() +{ + createX11Connection(); + auto *xcbConn = m_app->x11Connection(); + if (!xcbConn) { + // about to quit + Q_EMIT criticalError(1); + return; + } + QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcbConn), QSocketNotifier::Read, this); + auto processXcbEvents = [this, xcbConn] { + while (auto event = xcb_poll_for_event(xcbConn)) { + long result = 0; + QThread::currentThread()->eventDispatcher()->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result); + free(event); + } + xcb_flush(xcbConn); + }; + connect(notifier, &QSocketNotifier::activated, this, processXcbEvents); + connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents); + connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents); + + // create selection owner for WM_S0 - magic X display number expected by XWayland + KSelectionOwner owner("WM_S0", xcbConn, m_app->x11RootWindow()); + owner.claim(true); + + m_app->createAtoms(); + m_app->setupEventFilters(); + + // Check whether another windowmanager is running + const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT}; + ScopedCPointer redirectCheck(xcb_request_check(connection(), + xcb_change_window_attributes_checked(connection(), + rootWindow(), + XCB_CW_EVENT_MASK, + maskValues))); + if (!redirectCheck.isNull()) { + fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr); + Q_EMIT criticalError(1); + return; + } + + auto env = m_app->processStartupEnvironment(); + env.insert(QStringLiteral("DISPLAY"), QString::fromUtf8(qgetenv("DISPLAY"))); + m_app->setProcessStartupEnvironment(env); + + m_app->startSession(); + m_app->createWorkspace(); + + Xcb::sync(); // Trigger possible errors, there's still a chance to abort + + m_app->notifyKSplash(); +} + +} +} diff --git a/xwl/xwayland.h b/xwl/xwayland.h new file mode 100644 index 000000000..a46442858 --- /dev/null +++ b/xwl/xwayland.h @@ -0,0 +1,62 @@ +/******************************************************************** + 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_XWL_XWAYLAND +#define KWIN_XWL_XWAYLAND + +#include + +class QProcess; + +class xcb_screen_t; + +namespace KWin +{ +class ApplicationWayland; + +namespace Xwl +{ + +class Xwayland : public QObject +{ + Q_OBJECT +public: + Xwayland(ApplicationWayland *app, QObject *parent = nullptr); + virtual ~Xwayland(); + + void init(); + +Q_SIGNALS: + void criticalError(int code); + +private: + void createX11Connection(); + void continueStartupWithX(); + + int m_xcbConnectionFd = -1; + QProcess *m_xwaylandProcess = nullptr; + QMetaObject::Connection m_xwaylandFailConnection; + + ApplicationWayland *m_app; +}; + +} +} + +#endif