diff --git a/CMakeLists.txt b/CMakeLists.txt index 6963f9dc9a..3cf349d18b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,26 +506,42 @@ if(Wayland_Client_FOUND AND XKB_FOUND) endif() endif() -kf5_add_kdeinit_executable( kwin ${kwin_KDEINIT_SRCS}) +add_library(kwin SHARED ${kwin_KDEINIT_SRCS}) -target_link_libraries(kdeinit_kwin ${kwinLibs}) -set_target_properties(kwin PROPERTIES OUTPUT_NAME ${KWIN_INTERNAL_NAME_X11}) -generate_export_header(kdeinit_kwin EXPORT_FILE_NAME kwin_export.h) +set_target_properties(kwin PROPERTIES + VERSION GENERIC_LIB_VERSION + SOVERSION GENERIC_LIB_SOVERSION + ) + +target_link_libraries(kwin ${kwinLibs}) +generate_export_header(kwin EXPORT_FILE_NAME kwin_export.h) if(KWIN_BUILD_OPENGL) - target_link_libraries(kdeinit_kwin kwinglutils ${epoxy_LIBRARY}) + target_link_libraries(kwin kwinglutils ${epoxy_LIBRARY}) # -ldl used by OpenGL code find_library(DL_LIBRARY dl) if (DL_LIBRARY) - target_link_libraries(kdeinit_kwin ${DL_LIBRARY}) + target_link_libraries(kwin ${DL_LIBRARY}) endif() elseif(KWIN_BUILD_OPENGLES) - target_link_libraries(kdeinit_kwin ${kwinLibs} kwinglesutils ${epoxy_LIBRARY}) - set_target_properties(kdeinit_kwin PROPERTIES COMPILE_FLAGS "-DKWIN_HAVE_OPENGLES") + target_link_libraries(kwin ${kwinLibs} kwinglesutils ${epoxy_LIBRARY}) + set_target_properties(kwin PROPERTIES COMPILE_FLAGS "-DKWIN_HAVE_OPENGLES") endif() -install(TARGETS kdeinit_kwin ${INSTALL_TARGETS_DEFAULT_ARGS} ) -install(TARGETS kwin ${INSTALL_TARGETS_DEFAULT_ARGS} ) +kf5_add_kdeinit_executable(kwin_x11 main_x11.cpp) +target_link_libraries(kdeinit_kwin_x11 kwin) + +install(TARGETS kwin ${INSTALL_TARGETS_DEFAULT_ARGS} ) +install(TARGETS kdeinit_kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) +install(TARGETS kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} ) + +if(Wayland_Client_FOUND AND XKB_FOUND) + kf5_add_kdeinit_executable(kwin_wayland main_wayland.cpp) + target_link_libraries(kdeinit_kwin_wayland kwin) + + install(TARGETS kdeinit_kwin_wayland ${INSTALL_TARGETS_DEFAULT_ARGS} ) + install(TARGETS kwin_wayland ${INSTALL_TARGETS_DEFAULT_ARGS} ) +endif() ########### install files ############### diff --git a/libkwineffects/kwinglobals.h b/libkwineffects/kwinglobals.h index 041b488c7b..8c9b5cb52f 100644 --- a/libkwineffects/kwinglobals.h +++ b/libkwineffects/kwinglobals.h @@ -30,7 +30,6 @@ along with this program. If not, see . #include #define KWIN_QT5_PORTING 0 -#define KWIN_EXPORT KDEINIT_KWIN_EXPORT namespace KWin { diff --git a/main.cpp b/main.cpp index 76fc78eaf8..9aeddd96a8 100644 --- a/main.cpp +++ b/main.cpp @@ -26,7 +26,6 @@ along with this program. If not, see . #include "options.h" #include "sm.h" #include "workspace.h" -#include "xcbutils.h" // KDE #include @@ -48,9 +47,6 @@ along with this program. If not, see . #include #include #include -#include -// TODO: remove once QX11Info provides the X screen -#include // system #ifdef HAVE_UNISTD_H @@ -71,68 +67,6 @@ Atoms* atoms; int screen_number = -1; bool is_multihead = false; -//************************************ -// KWinSelectionOwner -//************************************ - -KWinSelectionOwner::KWinSelectionOwner(int screen_P) - : KSelectionOwner(make_selection_atom(screen_P), screen_P) -{ -} - -xcb_atom_t KWinSelectionOwner::make_selection_atom(int screen_P) -{ - if (screen_P < 0) - screen_P = QX11Info::appScreen(); - QByteArray screen(QByteArrayLiteral("WM_S")); - screen.append(QByteArray::number(screen_P)); - ScopedCPointer atom(xcb_intern_atom_reply( - connection(), - xcb_intern_atom_unchecked(connection(), false, screen.length(), screen.constData()), - nullptr)); - if (atom.isNull()) { - return XCB_ATOM_NONE; - } - return atom->atom; -} - -void KWinSelectionOwner::getAtoms() -{ - KSelectionOwner::getAtoms(); - if (xa_version == XCB_ATOM_NONE) { - const QByteArray name(QByteArrayLiteral("VERSION")); - ScopedCPointer atom(xcb_intern_atom_reply( - connection(), - xcb_intern_atom_unchecked(connection(), false, name.length(), name.constData()), - nullptr)); - if (!atom.isNull()) { - xa_version = atom->atom; - } - } -} - -void KWinSelectionOwner::replyTargets(xcb_atom_t property_P, xcb_window_t requestor_P) -{ - KSelectionOwner::replyTargets(property_P, requestor_P); - xcb_atom_t atoms[ 1 ] = { xa_version }; - // PropModeAppend ! - xcb_change_property(connection(), XCB_PROP_MODE_APPEND, requestor_P, - property_P, XCB_ATOM_ATOM, 32, 1, atoms); -} - -bool KWinSelectionOwner::genericReply(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P) -{ - if (target_P == xa_version) { - int32_t version[] = { 2, 0 }; - xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, requestor_P, - property_P, XCB_ATOM_INTEGER, 32, 2, version); - } else - return KSelectionOwner::genericReply(target_P, property_P, requestor_P); - return true; -} - -xcb_atom_t KWinSelectionOwner::xa_version = XCB_ATOM_NONE; - class AlternativeWMDialog : public QDialog { public: @@ -181,13 +115,31 @@ private: int Application::crashes = 0; -Application::Application(int &argc, char **argv) +bool Application::isX11MultiHead() +{ + return is_multihead; +} + +void Application::setX11MultiHead(bool multiHead) +{ + is_multihead = multiHead; +} + +void Application::setX11ScreenNumber(int screenNumber) +{ + screen_number = screenNumber; +} + +int Application::x11ScreenNumber() +{ + return screen_number; +} + +Application::Application(Application::OperationMode mode, int &argc, char **argv) : QApplication(argc, argv) - , owner() , m_eventFilter(new XcbEventFilter()) - , m_replace(false) , m_configLock(false) - , m_operationMode(OperationModeX11) + , m_operationMode(mode) { } @@ -196,11 +148,6 @@ void Application::setConfigLock(bool lock) m_configLock = lock; } -void Application::setReplace(bool replace) -{ - m_replace = replace; -} - Application::OperationMode Application::operationMode() const { return m_operationMode; @@ -232,67 +179,13 @@ void Application::start() config->reparseConfiguration(); } - if (screen_number == -1) - screen_number = QX11Info::appScreen(); - - owner.reset(new KWinSelectionOwner(screen_number)); - connect(owner.data(), &KSelectionOwner::failedToClaimOwnership, []{ - fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").toLocal8Bit().constData(), stderr); - ::exit(1); - }); - connect(owner.data(), SIGNAL(lostOwnership()), SLOT(lostSelection())); - connect(owner.data(), &KSelectionOwner::claimedOwnership, [this]{ - // we want all QQuickWindows with an alpha buffer - QQuickWindow::setDefaultAlphaBuffer(true); - - installNativeEventFilter(m_eventFilter.data()); - // first load options - done internally by a different thread - options = new Options; - - // 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: another window manager is running (try using --replace)\n").toLocal8Bit().constData(), stderr); - ::exit(1); - } - - atoms->retrieveHelpers(); - - // This tries to detect compositing options and can use GLX. GLX problems - // (X errors) shouldn't cause kwin to abort, so this is out of the - // critical startup section where x errors cause kwin to abort. - - // create workspace. - (void) new Workspace(isSessionRestored()); - - Xcb::sync(); // Trigger possible errors, there's still a chance to abort - - // Tell KSplash that KWin has started - QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), - QStringLiteral("/KSplash"), - QStringLiteral("org.kde.KSplash"), - QStringLiteral("setStage")); - ksplashProgressMessage.setArguments(QList() << QStringLiteral("wm")); - QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); - }); crashChecking(); - // we need to do an XSync here, otherwise the QPA might crash us later on - Xcb::sync(); - owner->claim(m_replace, true); - atoms = new Atoms; + performStartup(); } Application::~Application() { - delete Workspace::self(); - if (!owner.isNull() && owner->ownerWindow() != XCB_WINDOW_NONE) // If there was no --replace (no new WM) - Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); delete options; delete atoms; } @@ -328,15 +221,6 @@ void Application::crashChecking() QTimer::singleShot(15 * 1000, this, SLOT(resetCrashesCount())); } -void Application::lostSelection() -{ - sendPostedEvents(); - delete Workspace::self(); - // Remove windowmanager privileges - Xcb::selectInput(rootWindow(), XCB_EVENT_MASK_PROPERTY_CHANGE); - quit(); -} - bool Application::notify(QObject* o, QEvent* e) { if (Workspace::self()->workspaceEvent(e)) @@ -344,11 +228,6 @@ bool Application::notify(QObject* o, QEvent* e) return QApplication::notify(o, e); } -static void sighandler(int) -{ - QApplication::exit(); -} - void Application::crashHandler(int signal) { crashes++; @@ -377,26 +256,68 @@ bool Application::wasCrash() return crashes > 0; } -bool XcbEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) -{ - Q_UNUSED(result) - if (!Workspace::self()) { - // Workspace not yet created - return false; - } - if (eventType != "xcb_generic_event_t") { - return false; - } - return Workspace::self()->workspaceEvent(static_cast(message)); -} - -} // namespace - -static const char version[] = KWIN_VERSION_STRING; static const char description[] = I18N_NOOP("KDE window manager"); -extern "C" -KWIN_EXPORT int kdemain(int argc, char * argv[]) +void Application::createAboutData() +{ + KAboutData aboutData(QStringLiteral(KWIN_NAME), // The program name used internally + i18n("KWin"), // A displayable program name string + QStringLiteral(KWIN_VERSION_STRING), // The program version string + i18n(description), // Short description of what the app does + KAboutLicense::GPL, // The license this code is released under + i18n("(c) 1999-2013, The KDE Developers")); // Copyright Statement + + aboutData.addAuthor(i18n("Matthias Ettrich"), QString(), QStringLiteral("ettrich@kde.org")); + aboutData.addAuthor(i18n("Cristian Tibirna"), QString(), QStringLiteral("tibirna@kde.org")); + aboutData.addAuthor(i18n("Daniel M. Duley"), QString(), QStringLiteral("mosfet@kde.org")); + aboutData.addAuthor(i18n("Luboš Luňák"), QString(), QStringLiteral("l.lunak@kde.org")); + aboutData.addAuthor(i18n("Martin Gräßlin"), i18n("Maintainer"), QStringLiteral("mgraesslin@kde.org")); + KAboutData::setApplicationData(aboutData); +} + +static const QString s_lockOption = QStringLiteral("lock"); +static const QString s_crashesOption = QStringLiteral("crashes"); + +void Application::setupCommandLine(QCommandLineParser *parser) +{ + QCommandLineOption lockOption(s_lockOption, i18n("Disable configuration options")); + QCommandLineOption crashesOption(s_crashesOption, i18n("Indicate that KWin has recently crashed n times"), QStringLiteral("n")); + + parser->setApplicationDescription(i18n("KDE window manager")); + parser->addVersionOption(); + parser->addHelpOption(); + parser->addOption(lockOption); + parser->addOption(crashesOption); + KAboutData::applicationData().setupCommandLine(parser); +} + +void Application::processCommandLine(QCommandLineParser *parser) +{ + setConfigLock(parser->isSet(s_lockOption)); + Application::setCrashCount(parser->value(s_crashesOption).toInt()); +} + +void Application::registerDBusService() +{ + QString appname; + if (x11ScreenNumber() == 0) + appname = QStringLiteral("org.kde.kwin"); + else + appname.sprintf("org.kde.kwin-screen-%d", KWin::Application::x11ScreenNumber()); + + QDBusConnection::sessionBus().interface()->registerService( + appname, QDBusConnectionInterface::DontQueueService); +} + +void Application::setupTranslator() +{ + QTranslator qtTranslator; + qtTranslator.load("qt_" + QLocale::system().name(), + QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + installTranslator(&qtTranslator); +} + +void Application::setupMalloc() { #ifdef M_TRIM_THRESHOLD // Prevent fragmentation of the heap by malloc (glibc). @@ -412,146 +333,79 @@ KWIN_EXPORT int kdemain(int argc, char * argv[]) #endif // HAVE_UNISTD_H mallopt(M_TRIM_THRESHOLD, 5*pagesize); #endif // M_TRIM_THRESHOLD - - KLocalizedString::setApplicationDomain("kwin"); - QLoggingCategory::setFilterRules(QStringLiteral("aurorae.debug = true\n") + - QStringLiteral("kwineffects.debug = true")); - - int primaryScreen = 0; - xcb_connection_t *c = xcb_connect(nullptr, &primaryScreen); - if (!c || xcb_connection_has_error(c)) { - fprintf(stderr, "%s: FATAL ERROR while trying to open display %s\n", - argv[0], qgetenv("DISPLAY").constData()); - exit(1); - } - - const int number_of_screens = xcb_setup_roots_length(xcb_get_setup(c)); - - // multi head - auto isMultiHead = []() -> bool { - QByteArray multiHead = qgetenv("KDE_MULTIHEAD"); - if (!multiHead.isEmpty()) { - return (multiHead.toLower() == "true"); - } - return true; - }; - if (number_of_screens != 1 && isMultiHead()) { - KWin::is_multihead = true; - KWin::screen_number = primaryScreen; - int pos; // Temporarily needed to reconstruct DISPLAY var if multi-head - QByteArray display_name = qgetenv("DISPLAY"); - xcb_disconnect(c); - c = nullptr; - - if ((pos = display_name.lastIndexOf('.')) != -1) - display_name.remove(pos, 10); // 10 is enough to be sure we removed ".s" - - QString envir; - for (int i = 0; i < number_of_screens; i++) { - // If execution doesn't pass by here, then kwin - // acts exactly as previously - if (i != KWin::screen_number && fork() == 0) { - KWin::screen_number = i; - // Break here because we are the child process, we don't - // want to fork() anymore - break; - } - } - // In the next statement, display_name shouldn't contain a screen - // number. If it had it, it was removed at the "pos" check - envir.sprintf("DISPLAY=%s.%d", display_name.data(), KWin::screen_number); - - if (putenv(strdup(envir.toAscii().constData()))) { - fprintf(stderr, "%s: WARNING: unable to set DISPLAY environment variable\n", argv[0]); - perror("putenv()"); - } - } - - 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); - - // Disable the glib event loop integration, since it seems to be responsible - // for several bug reports about high CPU usage (bug #239963) - setenv("QT_NO_GLIB", "1", true); - - // enforce xcb plugin, unfortunately command line switch has precedence - setenv("QT_QPA_PLATFORM", "xcb", true); - - KWin::Application a(argc, argv); - - QTranslator qtTranslator; - qtTranslator.load("qt_" + QLocale::system().name(), - QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - a.installTranslator(&qtTranslator); - - KAboutData aboutData(QStringLiteral(KWIN_NAME), // The program name used internally - i18n("KWin"), // A displayable program name string - QStringLiteral(KWIN_VERSION_STRING), // The program version string - i18n(description), // Short description of what the app does - KAboutLicense::GPL, // The license this code is released under - i18n("(c) 1999-2013, The KDE Developers")); // Copyright Statement - - aboutData.addAuthor(i18n("Matthias Ettrich"), QString(), QStringLiteral("ettrich@kde.org")); - aboutData.addAuthor(i18n("Cristian Tibirna"), QString(), QStringLiteral("tibirna@kde.org")); - aboutData.addAuthor(i18n("Daniel M. Duley"), QString(), QStringLiteral("mosfet@kde.org")); - aboutData.addAuthor(i18n("Luboš Luňák"), QString(), QStringLiteral("l.lunak@kde.org")); - aboutData.addAuthor(i18n("Martin Gräßlin"), i18n("Maintainer"), QStringLiteral("mgraesslin@kde.org")); - KAboutData::setApplicationData(aboutData); - - QCommandLineOption lockOption(QStringLiteral("lock"), i18n("Disable configuration options")); - QCommandLineOption replaceOption(QStringLiteral("replace"), i18n("Replace already-running ICCCM2.0-compliant window manager")); - QCommandLineOption crashesOption(QStringLiteral("crashes"), i18n("Indicate that KWin has recently crashed n times"), QStringLiteral("n")); - - QCommandLineParser parser; - parser.setApplicationDescription(i18n("KDE window manager")); - parser.addVersionOption(); - parser.addHelpOption(); - parser.addOption(lockOption); - parser.addOption(replaceOption); - parser.addOption(crashesOption); - aboutData.setupCommandLine(&parser); - - parser.process(a); - aboutData.processCommandLine(&parser); - - KWin::Application::setCrashCount(parser.value(crashesOption).toInt()); - a.setConfigLock(parser.isSet(lockOption)); - a.setReplace(parser.isSet(replaceOption)); - - // perform sanity checks - if (a.platformName().toLower() != QStringLiteral("xcb")) { - fprintf(stderr, "%s: FATAL ERROR expecting platform xcb but got platform %s\n", - argv[0], qPrintable(a.platformName())); - exit(1); - } - if (!KWin::display()) { - fprintf(stderr, "%s: FATAL ERROR KWin requires Xlib support in the xcb plugin. Do not configure Qt with -no-xcb-xlib\n", - argv[0]); - exit(1); - } - - a.start(); - -#warning SessionManager needs porting -#if KWIN_QT5_PORTING - KWin::SessionManager weAreIndeed; -#endif - KWin::SessionSaveDoneHelper helper; - - QString appname; - if (KWin::screen_number == 0) - appname = QStringLiteral("org.kde.kwin"); - else - appname.sprintf("org.kde.kwin-screen-%d", KWin::screen_number); - - QDBusConnection::sessionBus().interface()->registerService( - appname, QDBusConnectionInterface::DontQueueService); - - return a.exec(); } +void Application::setupLocalizedString() +{ + KLocalizedString::setApplicationDomain("kwin"); +} + +void Application::setupLoggingCategoryFilters() +{ + QLoggingCategory::setFilterRules(QStringLiteral("aurorae.debug = true\n") + + QStringLiteral("kwineffects.debug = true")); +} + +void Application::notifyKSplash() +{ + // Tell KSplash that KWin has started + QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), + QStringLiteral("/KSplash"), + QStringLiteral("org.kde.KSplash"), + QStringLiteral("setStage")); + ksplashProgressMessage.setArguments(QList() << QStringLiteral("wm")); + QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); +} + +void Application::createWorkspace() +{ + // ensure the helper atoms are retrieved before we create the Workspace + atoms->retrieveHelpers(); + + // we want all QQuickWindows with an alpha buffer, do here as Workspace might create QQuickWindows + QQuickWindow::setDefaultAlphaBuffer(true); + + // This tries to detect compositing options and can use GLX. GLX problems + // (X errors) shouldn't cause kwin to abort, so this is out of the + // critical startup section where x errors cause kwin to abort. + + // create workspace. + (void) new Workspace(isSessionRestored()); +} + +void Application::createAtoms() +{ + atoms = new Atoms; +} + +void Application::createOptions() +{ + options = new Options; +} + +void Application::setupEventFilters() +{ + installNativeEventFilter(m_eventFilter.data()); +} + +void Application::destroyWorkspace() +{ + delete Workspace::self(); +} + +bool XcbEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) +{ + Q_UNUSED(result) + if (!Workspace::self()) { + // Workspace not yet created + return false; + } + if (eventType != "xcb_generic_event_t") { + return false; + } + return Workspace::self()->workspaceEvent(static_cast(message)); +} + +} // namespace + #include "main.moc" diff --git a/main.h b/main.h index db4f581139..dc71bac288 100644 --- a/main.h +++ b/main.h @@ -22,12 +22,16 @@ along with this program. If not, see . #ifndef MAIN_H #define MAIN_H +#include + #include #include // Qt #include #include +class QCommandLineParser; + namespace KWin { @@ -37,22 +41,7 @@ public: virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override; }; -class KWinSelectionOwner - : public KSelectionOwner -{ - Q_OBJECT -public: - explicit KWinSelectionOwner(int screen); -protected: - virtual bool genericReply(xcb_atom_t target, xcb_atom_t property, xcb_window_t requestor); - virtual void replyTargets(xcb_atom_t property, xcb_window_t requestor); - virtual void getAtoms(); -private: - xcb_atom_t make_selection_atom(int screen); - static xcb_atom_t xa_version; -}; - -class Application : public QApplication +class KWIN_EXPORT Application : public QApplication { Q_OBJECT public: @@ -73,10 +62,8 @@ public: */ OperationModeWaylandAndX11 }; - Application(int &argc, char **argv); - ~Application(); + virtual ~Application(); - void setReplace(bool replace); void setConfigLock(bool lock); void start(); @@ -90,22 +77,62 @@ public: bool shouldUseWaylandForCompositing() const; bool requiresCompositing() const; + void setupTranslator(); + void setupCommandLine(QCommandLineParser *parser); + void processCommandLine(QCommandLineParser *parser); + + void registerDBusService(); + static void setCrashCount(int count); static bool wasCrash(); + /** + * Creates the KAboutData object for the KWin instance and registers it as + * KAboutData::setApplicationData. + **/ + static void createAboutData(); + + /** + * @returns the X11 Screen number. If not applicable it's set to @c -1. + **/ + static int x11ScreenNumber(); + /** + * Sets the X11 screen number of this KWin instance to @p screenNumber. + **/ + static void setX11ScreenNumber(int screenNumber); + /** + * @returns whether this is a multi head setup on X11. + **/ + static bool isX11MultiHead(); + /** + * Sets whether this is a multi head setup on X11. + */ + static void setX11MultiHead(bool multiHead); + + static void setupMalloc(); + static void setupLocalizedString(); + static void setupLoggingCategoryFilters(); + protected: + Application(OperationMode mode, int &argc, char **argv); + virtual void performStartup() = 0; + + void notifyKSplash(); + void createWorkspace(); + void createAtoms(); + void createOptions(); + void setupEventFilters(); + void destroyWorkspace(); + bool notify(QObject* o, QEvent* e); static void crashHandler(int signal); private Q_SLOTS: - void lostSelection(); void resetCrashesCount(); private: void crashChecking(); - QScopedPointer owner; QScopedPointer m_eventFilter; - bool m_replace; bool m_configLock; OperationMode m_operationMode; static int crashes; diff --git a/main_wayland.cpp b/main_wayland.cpp new file mode 100644 index 0000000000..0baf84aa87 --- /dev/null +++ b/main_wayland.cpp @@ -0,0 +1,138 @@ +/******************************************************************** + 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 +// kwin +#include "xcbutils.h" + +// KDE +#include +// Qt +#include +#include + +// system +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +namespace KWin +{ + +//************************************ +// ApplicationWayland +//************************************ + +ApplicationWayland::ApplicationWayland(int &argc, char **argv) + : Application(OperationModeWaylandAndX11, argc, argv) +{ +} + +ApplicationWayland::~ApplicationWayland() +{ + destroyWorkspace(); + // TODO: only if we support X11 + Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); +} + +void ApplicationWayland::performStartup() +{ + // we don't support X11 multi-head in Wayland + Application::setX11ScreenNumber(0); + + // we need to do an XSync here, otherwise the QPA might crash us later on + // TODO: remove + Xcb::sync(); + + createAtoms(); + + setupEventFilters(); + // first load options - done internally by a different thread + createOptions(); + + // 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: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr); + ::exit(1); + } + + createWorkspace(); + + Xcb::sync(); // Trigger possible errors, there's still a chance to abort + + notifyKSplash(); +} + +} // namespace + +extern "C" +KWIN_EXPORT int kdemain(int argc, char * argv[]) +{ + KWin::Application::setupMalloc(); + KWin::Application::setupLocalizedString(); + KWin::Application::setupLoggingCategoryFilters(); + + // TODO: check whether we have a wayland connection + + // Disable the glib event loop integration, since it seems to be responsible + // for several bug reports about high CPU usage (bug #239963) + setenv("QT_NO_GLIB", "1", true); + + // enforce xcb plugin, unfortunately command line switch has precedence + // TODO: ensure it's not xcb once we support the Wayland QPA + setenv("QT_QPA_PLATFORM", "xcb", true); + + KWin::ApplicationWayland a(argc, argv); + a.setupTranslator(); + + KWin::Application::createAboutData(); + + QCommandLineParser parser; + a.setupCommandLine(&parser); + + parser.process(a); + a.processCommandLine(&parser); + + // perform sanity checks + // TODO: remove those two + if (a.platformName().toLower() != QStringLiteral("xcb")) { + fprintf(stderr, "%s: FATAL ERROR expecting platform xcb but got platform %s\n", + argv[0], qPrintable(a.platformName())); + exit(1); + } + if (!KWin::display()) { + fprintf(stderr, "%s: FATAL ERROR KWin requires Xlib support in the xcb plugin. Do not configure Qt with -no-xcb-xlib\n", + argv[0]); + exit(1); + } + + a.start(); + + // TODO: is this still needed? + a.registerDBusService(); + + return a.exec(); +} diff --git a/main_wayland.h b/main_wayland.h new file mode 100644 index 0000000000..a0fc82d057 --- /dev/null +++ b/main_wayland.h @@ -0,0 +1,40 @@ +/******************************************************************** + 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" + +namespace KWin +{ + +class ApplicationWayland : public Application +{ + Q_OBJECT +public: + ApplicationWayland(int &argc, char **argv); + virtual ~ApplicationWayland(); + +protected: + void performStartup() override; +}; + +} + +#endif diff --git a/main_x11.cpp b/main_x11.cpp new file mode 100644 index 0000000000..738486eb65 --- /dev/null +++ b/main_x11.cpp @@ -0,0 +1,295 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich +Copyright (C) 2003 Lubos Lunak +Copyright (C) 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_x11.h" +#include +// kwin +#include "sm.h" +#include "xcbutils.h" + +// KDE +#include +// Qt +#include +#include + +// system +#ifdef HAVE_UNISTD_H +#include +#endif // HAVE_UNISTD_H + +namespace KWin +{ + +static void sighandler(int) +{ + QApplication::exit(); +} + +//************************************ +// KWinSelectionOwner +//************************************ + +KWinSelectionOwner::KWinSelectionOwner(int screen_P) + : KSelectionOwner(make_selection_atom(screen_P), screen_P) +{ +} + +xcb_atom_t KWinSelectionOwner::make_selection_atom(int screen_P) +{ + if (screen_P < 0) + screen_P = QX11Info::appScreen(); + QByteArray screen(QByteArrayLiteral("WM_S")); + screen.append(QByteArray::number(screen_P)); + ScopedCPointer atom(xcb_intern_atom_reply( + connection(), + xcb_intern_atom_unchecked(connection(), false, screen.length(), screen.constData()), + nullptr)); + if (atom.isNull()) { + return XCB_ATOM_NONE; + } + return atom->atom; +} + +void KWinSelectionOwner::getAtoms() +{ + KSelectionOwner::getAtoms(); + if (xa_version == XCB_ATOM_NONE) { + const QByteArray name(QByteArrayLiteral("VERSION")); + ScopedCPointer atom(xcb_intern_atom_reply( + connection(), + xcb_intern_atom_unchecked(connection(), false, name.length(), name.constData()), + nullptr)); + if (!atom.isNull()) { + xa_version = atom->atom; + } + } +} + +void KWinSelectionOwner::replyTargets(xcb_atom_t property_P, xcb_window_t requestor_P) +{ + KSelectionOwner::replyTargets(property_P, requestor_P); + xcb_atom_t atoms[ 1 ] = { xa_version }; + // PropModeAppend ! + xcb_change_property(connection(), XCB_PROP_MODE_APPEND, requestor_P, + property_P, XCB_ATOM_ATOM, 32, 1, atoms); +} + +bool KWinSelectionOwner::genericReply(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P) +{ + if (target_P == xa_version) { + int32_t version[] = { 2, 0 }; + xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, requestor_P, + property_P, XCB_ATOM_INTEGER, 32, 2, version); + } else + return KSelectionOwner::genericReply(target_P, property_P, requestor_P); + return true; +} + +xcb_atom_t KWinSelectionOwner::xa_version = XCB_ATOM_NONE; + +//************************************ +// ApplicationX11 +//************************************ + +ApplicationX11::ApplicationX11(int &argc, char **argv) + : Application(OperationModeX11, argc, argv) + , owner() + , m_replace(false) +{ +} + +ApplicationX11::~ApplicationX11() +{ + destroyWorkspace(); + if (!owner.isNull() && owner->ownerWindow() != XCB_WINDOW_NONE) // If there was no --replace (no new WM) + Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT); +} + +void ApplicationX11::setReplace(bool replace) +{ + m_replace = replace; +} + +void ApplicationX11::lostSelection() +{ + sendPostedEvents(); + destroyWorkspace(); + // Remove windowmanager privileges + Xcb::selectInput(rootWindow(), XCB_EVENT_MASK_PROPERTY_CHANGE); + quit(); +} + +void ApplicationX11::performStartup() +{ + if (Application::x11ScreenNumber() == -1) { + Application::setX11ScreenNumber(QX11Info::appScreen()); + } + + owner.reset(new KWinSelectionOwner(Application::x11ScreenNumber())); + connect(owner.data(), &KSelectionOwner::failedToClaimOwnership, []{ + fputs(i18n("kwin: unable to claim manager selection, another wm running? (try using --replace)\n").toLocal8Bit().constData(), stderr); + ::exit(1); + }); + connect(owner.data(), SIGNAL(lostOwnership()), SLOT(lostSelection())); + connect(owner.data(), &KSelectionOwner::claimedOwnership, [this]{ + setupEventFilters(); + // first load options - done internally by a different thread + createOptions(); + + // 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: another window manager is running (try using --replace)\n").toLocal8Bit().constData(), stderr); + ::exit(1); + } + + createWorkspace(); + + Xcb::sync(); // Trigger possible errors, there's still a chance to abort + + notifyKSplash(); + }); + // we need to do an XSync here, otherwise the QPA might crash us later on + Xcb::sync(); + owner->claim(m_replace, true); + + createAtoms(); +} + +} // namespace + +extern "C" +KWIN_EXPORT int kdemain(int argc, char * argv[]) +{ + KWin::Application::setupMalloc(); + KWin::Application::setupLocalizedString(); + KWin::Application::setupLoggingCategoryFilters(); + + int primaryScreen = 0; + xcb_connection_t *c = xcb_connect(nullptr, &primaryScreen); + if (!c || xcb_connection_has_error(c)) { + fprintf(stderr, "%s: FATAL ERROR while trying to open display %s\n", + argv[0], qgetenv("DISPLAY").constData()); + exit(1); + } + + const int number_of_screens = xcb_setup_roots_length(xcb_get_setup(c)); + + // multi head + auto isMultiHead = []() -> bool { + QByteArray multiHead = qgetenv("KDE_MULTIHEAD"); + if (!multiHead.isEmpty()) { + return (multiHead.toLower() == "true"); + } + return true; + }; + if (number_of_screens != 1 && isMultiHead()) { + KWin::Application::setX11MultiHead(true); + KWin::Application::setX11ScreenNumber(primaryScreen); + int pos; // Temporarily needed to reconstruct DISPLAY var if multi-head + QByteArray display_name = qgetenv("DISPLAY"); + xcb_disconnect(c); + c = nullptr; + + if ((pos = display_name.lastIndexOf('.')) != -1) + display_name.remove(pos, 10); // 10 is enough to be sure we removed ".s" + + QString envir; + for (int i = 0; i < number_of_screens; i++) { + // If execution doesn't pass by here, then kwin + // acts exactly as previously + if (i != KWin::Application::x11ScreenNumber() && fork() == 0) { + KWin::Application::setX11ScreenNumber(i); + // Break here because we are the child process, we don't + // want to fork() anymore + break; + } + } + // In the next statement, display_name shouldn't contain a screen + // number. If it had it, it was removed at the "pos" check + envir.sprintf("DISPLAY=%s.%d", display_name.data(), KWin::Application::x11ScreenNumber()); + + if (putenv(strdup(envir.toAscii().constData()))) { + fprintf(stderr, "%s: WARNING: unable to set DISPLAY environment variable\n", argv[0]); + perror("putenv()"); + } + } + + 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); + + // Disable the glib event loop integration, since it seems to be responsible + // for several bug reports about high CPU usage (bug #239963) + setenv("QT_NO_GLIB", "1", true); + + // enforce xcb plugin, unfortunately command line switch has precedence + setenv("QT_QPA_PLATFORM", "xcb", true); + + KWin::ApplicationX11 a(argc, argv); + a.setupTranslator(); + + KWin::Application::createAboutData(); + + QCommandLineOption replaceOption(QStringLiteral("replace"), i18n("Replace already-running ICCCM2.0-compliant window manager")); + + QCommandLineParser parser; + a.setupCommandLine(&parser); + parser.addOption(replaceOption); + + parser.process(a); + a.processCommandLine(&parser); + a.setReplace(parser.isSet(replaceOption)); + + // perform sanity checks + if (a.platformName().toLower() != QStringLiteral("xcb")) { + fprintf(stderr, "%s: FATAL ERROR expecting platform xcb but got platform %s\n", + argv[0], qPrintable(a.platformName())); + exit(1); + } + if (!KWin::display()) { + fprintf(stderr, "%s: FATAL ERROR KWin requires Xlib support in the xcb plugin. Do not configure Qt with -no-xcb-xlib\n", + argv[0]); + exit(1); + } + + a.start(); + +#warning SessionManager needs porting +#if KWIN_QT5_PORTING + KWin::SessionManager weAreIndeed; +#endif + KWin::SessionSaveDoneHelper helper; + + // TODO: is this still needed? + a.registerDBusService(); + + return a.exec(); +} diff --git a/main_x11.h b/main_x11.h new file mode 100644 index 0000000000..ef3c44464f --- /dev/null +++ b/main_x11.h @@ -0,0 +1,64 @@ +/******************************************************************** + 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_X11_H +#define KWIN_MAIN_X11_H +#include "main.h" + +namespace KWin +{ + +class KWinSelectionOwner + : public KSelectionOwner +{ + Q_OBJECT +public: + explicit KWinSelectionOwner(int screen); +protected: + virtual bool genericReply(xcb_atom_t target, xcb_atom_t property, xcb_window_t requestor); + virtual void replyTargets(xcb_atom_t property, xcb_window_t requestor); + virtual void getAtoms(); +private: + xcb_atom_t make_selection_atom(int screen); + static xcb_atom_t xa_version; +}; + +class ApplicationX11 : public Application +{ + Q_OBJECT +public: + ApplicationX11(int &argc, char **argv); + virtual ~ApplicationX11(); + + void setReplace(bool replace); + +protected: + void performStartup() override; + +private Q_SLOTS: + void lostSelection(); + +private: + QScopedPointer owner; + bool m_replace; +}; + +} + +#endif diff --git a/sm.h b/sm.h index 7ab8bd5dff..cc63aa3da1 100644 --- a/sm.h +++ b/sm.h @@ -82,7 +82,7 @@ enum SMSavePhase { SMSavePhase2Full // complete saving in phase2, there was no phase 0 }; -class SessionSaveDoneHelper +class KWIN_EXPORT SessionSaveDoneHelper : public QObject { Q_OBJECT diff --git a/wayland_backend.cpp b/wayland_backend.cpp index 03f73a486b..7c2d57b24a 100644 --- a/wayland_backend.cpp +++ b/wayland_backend.cpp @@ -21,7 +21,6 @@ along with this program. If not, see . #include "wayland_backend.h" // KWin #include "cursor.h" -#include "main.h" #include "input.h" // Qt #include @@ -751,7 +750,6 @@ WaylandBackend::WaylandBackend(QObject *parent) } initConnection(); - kwinApp()->setOperationMode(Application::OperationModeWaylandAndX11); } WaylandBackend::~WaylandBackend()