File indexing completed on 2024-04-28 16:54:58
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 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include <QApplication> 0010 #include <QCommandLineParser> 0011 #include <QDBusConnection> 0012 #include <QDBusMessage> 0013 #include <QDebug> 0014 #include <QLoggingCategory> 0015 #include <QMessageBox> 0016 #include <QProcess> 0017 #include <QQmlDebuggingEnabler> 0018 #include <QQuickWindow> 0019 #include <QSessionManager> 0020 #include <QSurfaceFormat> 0021 0022 #include <KAboutData> 0023 0024 #ifdef WITH_KUSERFEEDBACKCORE 0025 #include "userfeedback.h" 0026 #endif 0027 0028 #include <kcrash.h> 0029 #include <kdbusservice.h> 0030 #include <klocalizedstring.h> 0031 #include <kworkspace.h> 0032 0033 #include "coronatesthelper.h" 0034 #include "debug.h" 0035 #include "shellcorona.h" 0036 #include "softwarerendernotifier.h" 0037 #include "standaloneappcorona.h" 0038 0039 #include <QDBusConnectionInterface> 0040 #include <QDir> 0041 0042 int main(int argc, char *argv[]) 0043 { 0044 #if QT_CONFIG(qml_debug) 0045 if (qEnvironmentVariableIsSet("PLASMA_ENABLE_QML_DEBUG")) { 0046 QQmlDebuggingEnabler debugger; 0047 } 0048 #endif 0049 0050 auto format = QSurfaceFormat::defaultFormat(); 0051 format.setOption(QSurfaceFormat::ResetNotification); 0052 QSurfaceFormat::setDefaultFormat(format); 0053 0054 // Plasma scales itself to font DPI 0055 // on X, where we don't have compositor scaling, this generally works fine. 0056 // also there are bugs on older Qt, especially when it comes to fractional scaling 0057 // there's advantages to disabling, and (other than small context menu icons) few advantages in enabling 0058 0059 // On wayland, it's different. Everything is simpler as all co-ordinates are in the same co-ordinate system 0060 // we don't have fractional scaling on the client so don't hit most the remaining bugs and 0061 // even if we don't use Qt scaling the compositor will try to scale us anyway so we have no choice 0062 if (!qEnvironmentVariableIsSet("PLASMA_USE_QT_SCALING")) { 0063 qunsetenv("QT_DEVICE_PIXEL_RATIO"); 0064 QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); 0065 } else { 0066 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 0067 } 0068 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); 0069 0070 QQuickWindow::setDefaultAlphaBuffer(true); 0071 0072 qputenv("QT_WAYLAND_DISABLE_FIXED_POSITIONS", {}); 0073 const bool qpaVariable = qEnvironmentVariableIsSet("QT_QPA_PLATFORM"); 0074 KWorkSpace::detectPlatform(argc, argv); 0075 QApplication app(argc, argv); 0076 if (!qpaVariable) { 0077 // don't leak the env variable to processes we start 0078 qunsetenv("QT_QPA_PLATFORM"); 0079 } 0080 qunsetenv("QT_WAYLAND_DISABLE_FIXED_POSITIONS"); 0081 0082 KLocalizedString::setApplicationDomain("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"), i18n("Plasma"), 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; 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 standaloneOption(QStringList() << QStringLiteral("a") << QStringLiteral("standalone"), 0111 i18n("Load plasmashell as a standalone application, needs the shell-plugin option to be specified")); 0112 0113 QCommandLineOption replaceOption({QStringLiteral("replace")}, i18n("Replace an existing instance")); 0114 0115 QCommandLineOption testOption(QStringList() << QStringLiteral("test"), 0116 i18n("Enables test mode and specifies the layout javascript file to set up the testing environment"), 0117 i18n("file"), 0118 QStringLiteral("layout.js")); 0119 0120 #ifdef WITH_KUSERFEEDBACKCORE 0121 QCommandLineOption feedbackOption(QStringList() << QStringLiteral("feedback"), i18n("Lists the available options for user feedback")); 0122 #endif 0123 cliOptions.addOption(dbgOption); 0124 cliOptions.addOption(noRespawnOption); 0125 cliOptions.addOption(shellPluginOption); 0126 cliOptions.addOption(standaloneOption); 0127 cliOptions.addOption(testOption); 0128 cliOptions.addOption(replaceOption); 0129 #ifdef WITH_KUSERFEEDBACKCORE 0130 cliOptions.addOption(feedbackOption); 0131 #endif 0132 0133 aboutData.setupCommandLine(&cliOptions); 0134 cliOptions.process(app); 0135 aboutData.processCommandLine(&cliOptions); 0136 0137 // don't let the first KJob terminate us 0138 QCoreApplication::setQuitLockEnabled(false); 0139 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0140 QGuiApplication::setFallbackSessionManagementEnabled(false); 0141 #endif 0142 0143 auto disableSessionManagement = [](QSessionManager &sm) { 0144 sm.setRestartHint(QSessionManager::RestartNever); 0145 }; 0146 QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); 0147 QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); 0148 0149 corona = new ShellCorona(&app); 0150 corona->setShell(cliOptions.value(shellPluginOption)); 0151 if (!corona->kPackage().isValid()) { 0152 qCritical() << "starting invalid corona" << corona->shell(); 0153 return 1; 0154 } 0155 0156 #ifdef WITH_KUSERFEEDBACKCORE 0157 auto userFeedback = new UserFeedback(corona, &app); 0158 if (cliOptions.isSet(feedbackOption)) { 0159 QTextStream(stdout) << userFeedback->describeDataSources(); 0160 return 0; 0161 } 0162 #endif 0163 0164 QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, corona, &QObject::deleteLater); 0165 0166 if (!cliOptions.isSet(noRespawnOption) && !cliOptions.isSet(testOption)) { 0167 KCrash::setFlags(KCrash::AutoRestart); 0168 } 0169 0170 if (cliOptions.isSet(testOption)) { 0171 const QUrl layoutUrl = QUrl::fromUserInput(cliOptions.value(testOption), {}, QUrl::AssumeLocalFile); 0172 if (!layoutUrl.isLocalFile()) { 0173 qCWarning(PLASMASHELL) << "ensure the layout file is local" << layoutUrl; 0174 cliOptions.showHelp(1); 0175 } 0176 0177 QStandardPaths::setTestModeEnabled(true); 0178 QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).removeRecursively(); 0179 corona->setTestModeLayout(layoutUrl.toLocalFile()); 0180 0181 qApp->setProperty("org.kde.KActivities.core.disableAutostart", true); 0182 0183 new CoronaTestHelper(corona); 0184 } 0185 0186 if (cliOptions.isSet(standaloneOption)) { 0187 if (cliOptions.isSet(shellPluginOption)) { 0188 app.setApplicationName(QStringLiteral("plasmashell_") + cliOptions.value(shellPluginOption)); 0189 app.setQuitOnLastWindowClosed(true); 0190 0191 KDBusService service(KDBusService::Unique); 0192 // This will not leak, because corona deletes itself on window close 0193 new StandaloneAppCorona(cliOptions.value(shellPluginOption)); 0194 return app.exec(); 0195 } else { 0196 cliOptions.showHelp(1); 0197 } 0198 } else { 0199 // Tells libnotificationmanager that we're the only true application that may own notification and job progress services 0200 qApp->setProperty("_plasma_dbus_master", true); 0201 } 0202 0203 QObject::connect(corona, &ShellCorona::glInitializationFailed, &app, [&app]() { 0204 // scene graphs errors come from a thread 0205 // even though we process them in the main thread, app.exit could still process these events 0206 static bool s_multipleInvokations = false; 0207 if (s_multipleInvokations) { 0208 return; 0209 } 0210 s_multipleInvokations = true; 0211 0212 qCritical("Open GL context could not be created"); 0213 auto configGroup = KSharedConfig::openConfig()->group("QtQuickRendererSettings"); 0214 if (configGroup.readEntry("SceneGraphBackend") != QLatin1String("software")) { 0215 configGroup.writeEntry("SceneGraphBackend", "software", KConfigBase::Global | KConfigBase::Persistent); 0216 configGroup.sync(); 0217 QProcess::startDetached(QStringLiteral("plasmashell"), app.arguments()); 0218 } else { 0219 QCoreApplication::setAttribute(Qt::AA_ForceRasterWidgets); 0220 QMessageBox::critical(nullptr, 0221 i18n("Plasma Failed To Start"), 0222 i18n("Plasma is unable to start as it could not correctly use OpenGL 2 or software fallback\nPlease check that your " 0223 "graphic drivers are set up correctly.")); 0224 } 0225 app.exit(-1); 0226 }); 0227 replace = cliOptions.isSet(replaceOption); 0228 } 0229 0230 KDBusService service(KDBusService::Unique | KDBusService::StartupOption(replace ? KDBusService::Replace : 0)); 0231 0232 corona->init(); 0233 SoftwareRendererNotifier::notifyIfRelevant(); 0234 0235 return app.exec(); 0236 }