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 }