File indexing completed on 2024-05-05 09:56:36
0001 /* 0002 SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org> 0003 SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org> 0004 SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org> 0005 SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <csignal> 0011 0012 #include <QApplication> 0013 #include <QCommandLineParser> 0014 #include <QDBusConnection> 0015 #include <QDBusMessage> 0016 #include <QDebug> 0017 #include <QLoggingCategory> 0018 #include <QMessageBox> 0019 #include <QProcess> 0020 #include <QQmlDebuggingEnabler> 0021 #include <QQuickWindow> 0022 #include <QSessionManager> 0023 #include <QSurfaceFormat> 0024 0025 #include <KAboutData> 0026 #include <KSignalHandler> 0027 0028 #ifdef WITH_KUSERFEEDBACKCORE 0029 #include "userfeedback.h" 0030 #endif 0031 0032 #include <kcrash.h> 0033 #include <kdbusservice.h> 0034 #include <klocalizedstring.h> 0035 0036 #include "coronatesthelper.h" 0037 #include "debug.h" 0038 #include "shellcorona.h" 0039 #include "softwarerendernotifier.h" 0040 0041 #include <QDBusConnectionInterface> 0042 #include <QDir> 0043 0044 int main(int argc, char *argv[]) 0045 { 0046 #if QT_CONFIG(qml_debug) 0047 if (qEnvironmentVariableIsSet("PLASMA_ENABLE_QML_DEBUG")) { 0048 QQmlDebuggingEnabler debugger; 0049 } 0050 #endif 0051 0052 auto format = QSurfaceFormat::defaultFormat(); 0053 format.setOption(QSurfaceFormat::ResetNotification); 0054 QSurfaceFormat::setDefaultFormat(format); 0055 0056 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); 0057 0058 QQuickWindow::setDefaultAlphaBuffer(true); 0059 0060 // this works around a bug of Qt and the plasmashell protocol 0061 // consider disabling when layer-shell lands 0062 qputenv("QT_WAYLAND_DISABLE_FIXED_POSITIONS", {}); 0063 // this variable controls whether to reconnect or exit if the compositor dies, given plasmashell does a lot of 0064 // bespoke wayland code disable for now. consider disabling when layer-shell lands 0065 qunsetenv("QT_WAYLAND_RECONNECT"); 0066 QApplication app(argc, argv); 0067 0068 qunsetenv("QT_WAYLAND_DISABLE_FIXED_POSITIONS"); 0069 qputenv("QT_WAYLAND_RECONNECT", "1"); 0070 0071 KCrash::initialize(); 0072 0073 // Quit on SIGTERM to properly save state. See systemd.kill(5). 0074 // https://bugs.kde.org/show_bug.cgi?id=470604 0075 KSignalHandler::self()->watchSignal(SIGTERM); 0076 QObject::connect(KSignalHandler::self(), &KSignalHandler::signalReceived, &app, [&app](int signal) { 0077 if (signal == SIGTERM) { 0078 app.quit(); 0079 } 0080 }); 0081 0082 KLocalizedString::setApplicationDomain(QByteArrayLiteral("plasmashell")); 0083 0084 // The executable's path is added to the library/plugin paths. 0085 // This does not make much sense for plasmashell. 0086 app.removeLibraryPath(QCoreApplication::applicationDirPath()); 0087 0088 KAboutData aboutData(QStringLiteral("plasmashell"), QString(), QStringLiteral(PROJECT_VERSION), i18n("Plasma Shell"), KAboutLicense::GPL); 0089 0090 KAboutData::setApplicationData(aboutData); 0091 0092 app.setQuitOnLastWindowClosed(false); 0093 0094 bool replace = false; 0095 0096 ShellCorona *corona = nullptr; 0097 { 0098 QCommandLineParser cliOptions; 0099 0100 QCommandLineOption dbgOption(QStringList() << QStringLiteral("d") << QStringLiteral("qmljsdebugger"), i18n("Enable QML Javascript debugger")); 0101 0102 QCommandLineOption noRespawnOption(QStringList() << QStringLiteral("n") << QStringLiteral("no-respawn"), 0103 i18n("Do not restart plasma-shell automatically after a crash")); 0104 0105 QCommandLineOption shellPluginOption(QStringList() << QStringLiteral("p") << QStringLiteral("shell-plugin"), 0106 i18n("Force loading the given shell plugin"), 0107 QStringLiteral("plugin"), 0108 ShellCorona::defaultShell()); 0109 0110 QCommandLineOption replaceOption({QStringLiteral("replace")}, i18n("Replace an existing instance")); 0111 0112 QCommandLineOption testOption(QStringList() << QStringLiteral("test"), 0113 i18n("Enables test mode and specifies the layout javascript file to set up the testing environment"), 0114 i18n("file"), 0115 QStringLiteral("layout.js")); 0116 0117 #ifdef WITH_KUSERFEEDBACKCORE 0118 QCommandLineOption feedbackOption(QStringList() << QStringLiteral("feedback"), i18n("Lists the available options for user feedback")); 0119 #endif 0120 cliOptions.addOption(dbgOption); 0121 cliOptions.addOption(noRespawnOption); 0122 cliOptions.addOption(shellPluginOption); 0123 cliOptions.addOption(testOption); 0124 cliOptions.addOption(replaceOption); 0125 #ifdef WITH_KUSERFEEDBACKCORE 0126 cliOptions.addOption(feedbackOption); 0127 #endif 0128 0129 aboutData.setupCommandLine(&cliOptions); 0130 cliOptions.process(app); 0131 aboutData.processCommandLine(&cliOptions); 0132 0133 // don't let the first KJob terminate us 0134 QCoreApplication::setQuitLockEnabled(false); 0135 0136 auto disableSessionManagement = [](QSessionManager &sm) { 0137 sm.setRestartHint(QSessionManager::RestartNever); 0138 }; 0139 QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); 0140 QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); 0141 0142 corona = new ShellCorona(&app); 0143 corona->setShell(cliOptions.value(shellPluginOption)); 0144 if (!corona->kPackage().isValid()) { 0145 qCritical() << "starting invalid corona" << corona->shell(); 0146 return 1; 0147 } 0148 0149 #ifdef WITH_KUSERFEEDBACKCORE 0150 auto userFeedback = new UserFeedback(corona, &app); 0151 if (cliOptions.isSet(feedbackOption)) { 0152 QTextStream(stdout) << userFeedback->describeDataSources(); 0153 return 0; 0154 } 0155 #endif 0156 0157 QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, corona, &QObject::deleteLater); 0158 0159 if (!cliOptions.isSet(noRespawnOption) && !cliOptions.isSet(testOption)) { 0160 KCrash::setFlags(KCrash::AutoRestart); 0161 } 0162 0163 if (cliOptions.isSet(testOption)) { 0164 const QUrl layoutUrl = QUrl::fromUserInput(cliOptions.value(testOption), {}, QUrl::AssumeLocalFile); 0165 if (!layoutUrl.isLocalFile()) { 0166 qCWarning(PLASMASHELL) << "ensure the layout file is local" << layoutUrl; 0167 cliOptions.showHelp(1); 0168 } 0169 0170 QStandardPaths::setTestModeEnabled(true); 0171 QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).removeRecursively(); 0172 corona->setTestModeLayout(layoutUrl.toLocalFile()); 0173 0174 qApp->setProperty("org.kde.KActivities.core.disableAutostart", true); 0175 0176 new CoronaTestHelper(corona); 0177 } 0178 0179 // Tells libnotificationmanager that we're the only true application that may own notification and job progress services 0180 qApp->setProperty("_plasma_dbus_master", true); 0181 0182 QObject::connect(corona, &ShellCorona::glInitializationFailed, &app, [&app]() { 0183 // scene graphs errors come from a thread 0184 // even though we process them in the main thread, app.exit could still process these events 0185 static bool s_multipleInvokations = false; 0186 if (s_multipleInvokations) { 0187 return; 0188 } 0189 s_multipleInvokations = true; 0190 0191 qCritical("Open GL context could not be created"); 0192 auto configGroup = KSharedConfig::openConfig()->group(QStringLiteral("QtQuickRendererSettings")); 0193 if (configGroup.readEntry("SceneGraphBackend") != QLatin1String("software")) { 0194 configGroup.writeEntry("SceneGraphBackend", "software", KConfigBase::Global | KConfigBase::Persistent); 0195 configGroup.sync(); 0196 QProcess::startDetached(QStringLiteral("plasmashell"), app.arguments()); 0197 } else { 0198 QCoreApplication::setAttribute(Qt::AA_ForceRasterWidgets); 0199 QMessageBox::critical(nullptr, 0200 i18n("Plasma Failed To Start"), 0201 i18n("Plasma is unable to start as it could not correctly use OpenGL 2 or software fallback\nPlease check that your " 0202 "graphic drivers are set up correctly.")); 0203 } 0204 app.exit(-1); 0205 }); 0206 replace = cliOptions.isSet(replaceOption); 0207 } 0208 0209 KDBusService service(KDBusService::Unique | KDBusService::StartupOption(replace ? KDBusService::Replace : 0)); 0210 0211 corona->init(); 0212 SoftwareRendererNotifier::notifyIfRelevant(); 0213 0214 return app.exec(); 0215 }