main.cpp 8.86 KB
Newer Older
Sebastian Kügler's avatar
Sebastian Kügler committed
1
2
/*
 *  Copyright 2012 Marco Martin <mart@kde.org>
3
 *  Copyright 2013 Sebastian Kügler <sebas@kde.org>
4
 *  Copyright 2015 David Edmundson <davidedmundson@kde.org>
Sebastian Kügler's avatar
Sebastian Kügler committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 *  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, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <QApplication>
22
#include <QCommandLineParser>
23
#include <QQuickWindow>
24
#include <QSessionManager>
25
#include <QDebug>
26
#include <QProcess>
27
#include <QMessageBox>
David Edmundson's avatar
David Edmundson committed
28
29
#include <QDBusConnection>
#include <QDBusMessage>
30

Bhushan Shah's avatar
Bhushan Shah committed
31
#include <KAboutData>
David Edmundson's avatar
David Edmundson committed
32
#include <KQuickAddons/QtQuickSettings>
Sebastian Kügler's avatar
Sebastian Kügler committed
33

34
#include <kdbusservice.h>
Sebastian Kügler's avatar
Sebastian Kügler committed
35
#include <klocalizedstring.h>
36
#include <kcrash.h>
37
#include <kworkspace.h>
38

39
#include "shellcorona.h"
40
#include "standaloneappcorona.h"
41
#include "coronatesthelper.h"
42
#include "softwarerendernotifier.h"
43
44

#include <QDir>
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
45
#include <QDBusConnectionInterface>
Marco Martin's avatar
Marco Martin committed
46

47
int main(int argc, char *argv[])
Sebastian Kügler's avatar
Sebastian Kügler committed
48
{
David Edmundson's avatar
David Edmundson committed
49
50
51
52
    //Plasma scales itself to font DPI
    //on X, where we don't have compositor scaling, this generally works fine.
    //also there are bugs on older Qt, especially when it comes to fractional scaling
    //there's advantages to disabling, and (other than small context menu icons) few advantages in enabling
53

David Edmundson's avatar
David Edmundson committed
54
55
56
    //On wayland, it's different. Everything is simpler as all co-ordinates are in the same co-ordinate system
    //we don't have fractional scaling on the client so don't hit most the remaining bugs and
    //even if we don't use Qt scaling the compositor will try to scale us anyway so we have no choice
57
    if (!qEnvironmentVariableIsSet("PLASMA_USE_QT_SCALING")) {
David Edmundson's avatar
David Edmundson committed
58
59
        qunsetenv("QT_DEVICE_PIXEL_RATIO");
        QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
60
61
    } else {
        QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
David Edmundson's avatar
David Edmundson committed
62
    }
63

64
    QQuickWindow::setDefaultAlphaBuffer(true);
65

66
    const bool qpaVariable = qEnvironmentVariableIsSet("QT_QPA_PLATFORM");
67
    KWorkSpace::detectPlatform(argc, argv);
Sebastian Kügler's avatar
Sebastian Kügler committed
68
    QApplication app(argc, argv);
69
70
71
72
    if (!qpaVariable) {
        // don't leak the env variable to processes we start
        qunsetenv("QT_QPA_PLATFORM");
    }
73
    KLocalizedString::setApplicationDomain("plasmashell");
Bhushan Shah's avatar
Bhushan Shah committed
74

75
76
77
78
    // The executable's path is added to the library/plugin paths.
    // This does not make much sense for plasmashell.
    app.removeLibraryPath(QCoreApplication::applicationDirPath());

79
80
    KQuickAddons::QtQuickSettings::init();

81
    KAboutData aboutData(QStringLiteral("plasmashell"),
82
                         i18n("Plasma"),
Lukáš Tinkl's avatar
Lukáš Tinkl committed
83
84
85
                         QStringLiteral(PROJECT_VERSION),
                         i18n("Plasma Shell"),
                         KAboutLicense::GPL);
Bhushan Shah's avatar
Bhushan Shah committed
86
87
88

    KAboutData::setApplicationData(aboutData);

89
    app.setQuitOnLastWindowClosed(false);
Sebastian Kügler's avatar
Sebastian Kügler committed
90

Marco Martin's avatar
Marco Martin committed
91
    KSharedConfig::Ptr startupConf = KSharedConfig::openConfig(QStringLiteral("plasmashellrc"));
92
93
94
    KConfigGroup startupConfGroup(startupConf, "Shell");
    const QString defaultShell = startupConfGroup.readEntry("ShellPackage", "org.kde.plasma.desktop");

95
    bool replace = false;
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
96
    {
97
    QCommandLineParser cliOptions;
Sebastian Kügler's avatar
Sebastian Kügler committed
98

99
100
101
    QCommandLineOption dbgOption(QStringList() << QStringLiteral("d") <<
                                 QStringLiteral("qmljsdebugger"),
                                 i18n("Enable QML Javascript debugger"));
102

103
    QCommandLineOption noRespawnOption(QStringList() << QStringLiteral("n") <<
104
105
                                     QStringLiteral("no-respawn"),
                                     i18n("Do not restart plasma-shell automatically after a crash"));
Marco Martin's avatar
Marco Martin committed
106

107
108
    QCommandLineOption shellPluginOption(QStringList() << QStringLiteral("p") << QStringLiteral("shell-plugin"),
                                         i18n("Force loading the given shell plugin"),
109
                                         QStringLiteral("plugin"), defaultShell);
110

111
    QCommandLineOption standaloneOption(QStringList() << QStringLiteral("a") << QStringLiteral("standalone"),
Lukáš Tinkl's avatar
Lukáš Tinkl committed
112
                                        i18n("Load plasmashell as a standalone application, needs the shell-plugin option to be specified"));
113

David Edmundson's avatar
David Edmundson committed
114
115
    QCommandLineOption replaceOption({QStringLiteral("replace")},
                                 i18n("Replace an existing instance"));
116
117
118
119

    QCommandLineOption testOption(QStringList() << QStringLiteral("test"),
                                        i18n("Enables test mode and specifies the layout javascript file to set up the testing environment"), i18n("file"), QStringLiteral("layout.js"));

120
    cliOptions.addOption(dbgOption);
121
    cliOptions.addOption(noRespawnOption);
122
    cliOptions.addOption(shellPluginOption);
123
    cliOptions.addOption(standaloneOption);
124
    cliOptions.addOption(testOption);
David Edmundson's avatar
David Edmundson committed
125
    cliOptions.addOption(replaceOption);
Sebastian Kügler's avatar
Sebastian Kügler committed
126

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
127
    aboutData.setupCommandLine(&cliOptions);
128
    cliOptions.process(app);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
129
    aboutData.processCommandLine(&cliOptions);
Bhushan Shah's avatar
Bhushan Shah committed
130

131
    QGuiApplication::setFallbackSessionManagementEnabled(false);
132

133
134
135
136
137
138
    auto disableSessionManagement = [](QSessionManager &sm) {
        sm.setRestartHint(QSessionManager::RestartNever);
    };
    QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement);
    QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement);

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
139
140
    ShellCorona* corona = new ShellCorona(&app);
    corona->setShell(cliOptions.value(shellPluginOption));
141
    QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, corona, &QObject::deleteLater);
142

143
    if (!cliOptions.isSet(noRespawnOption) && !cliOptions.isSet(testOption)) {
144
        KCrash::setFlags(KCrash::AutoRestart);
145
146
    }

147
148
149
150
151
152
153
154
155
    if (cliOptions.isSet(testOption)) {
        const QUrl layoutUrl = QUrl::fromUserInput(cliOptions.value(testOption), {}, QUrl::AssumeLocalFile);
        if (!layoutUrl.isLocalFile()) {
            qWarning() << "ensure the layout file is local" << layoutUrl;
            cliOptions.showHelp(1);
        }

        QStandardPaths::setTestModeEnabled(true);
        QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).removeRecursively();
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
156
        corona->setTestModeLayout(layoutUrl.toLocalFile());
157

158
159
        qApp->setProperty("org.kde.KActivities.core.disableAutostart", true);

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
160
        new CoronaTestHelper(corona);
161
162
    }

Marco Martin's avatar
Marco Martin committed
163
164
    if (cliOptions.isSet(standaloneOption)) {
        if (cliOptions.isSet(shellPluginOption)) {
Laurent Montel's avatar
Laurent Montel committed
165
            app.setApplicationName(QStringLiteral("plasmashell_") + cliOptions.value(shellPluginOption));
166
            app.setQuitOnLastWindowClosed(true);
Marco Martin's avatar
Marco Martin committed
167
168

            KDBusService service(KDBusService::Unique);
169
            //This will not leak, because corona deletes itself on window close
David Edmundson's avatar
David Edmundson committed
170
            new StandaloneAppCorona(cliOptions.value(shellPluginOption));
Marco Martin's avatar
Marco Martin committed
171
172
173
174
            return app.exec();
        } else {
            cliOptions.showHelp(1);
        }
175
176
177
    } else {
        // Tells libnotificationmanager that we're the only true application that may own notification and job progress services
        qApp->setProperty("_plasma_dbus_master", true);
178
    }
David Edmundson's avatar
David Edmundson committed
179

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
180
    QObject::connect(corona, &ShellCorona::glInitializationFailed, &app, [&app]() {
181
182
183
184
185
186
187
188
189
190
191
192
193
        //scene graphs errors come from a thread
        //even though we process them in the main thread, app.exit could still process these events
        static bool s_multipleInvokations = false;
        if (s_multipleInvokations) {
            return;
        }
        s_multipleInvokations = true;

        qCritical("Open GL context could not be created");
        auto configGroup = KSharedConfig::openConfig()->group("QtQuickRendererSettings");
        if (configGroup.readEntry("SceneGraphBackend") != QLatin1String("software")) {
            configGroup.writeEntry("SceneGraphBackend", "software", KConfigBase::Global | KConfigBase::Persistent);
            configGroup.sync();
Laurent Montel's avatar
Laurent Montel committed
194
            QProcess::startDetached(QStringLiteral("plasmashell"), app.arguments());
195
196
197
198
199
200
201
        } else {
            QCoreApplication::setAttribute(Qt::AA_ForceRasterWidgets);
            QMessageBox::critical(nullptr, i18n("Plasma Failed To Start"),
                          i18n("Plasma is unable to start as it could not correctly use OpenGL 2 or software fallback\nPlease check that your graphic drivers are set up correctly."));
        }
        app.exit(-1);
    });
202
    replace = cliOptions.isSet(replaceOption);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
203
204
    }

205
    KDBusService service(KDBusService::Unique | KDBusService::StartupOption(replace ? KDBusService::Replace : 0));
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
206

207
    SoftwareRendererNotifier::notifyIfRelevant();
Sebastian Kügler's avatar
Sebastian Kügler committed
208

Sebastian Kügler's avatar
Sebastian Kügler committed
209
210
    return app.exec();
}