File indexing completed on 2024-04-21 04:52:35

0001 /*
0002     SPDX-FileCopyrightText: 2007 Marco Gittler <g.marco@freenet.de>
0003     SPDX-FileCopyrightText: 2008 Jean-Baptiste Mardelle <jb@kdenlive.org>
0004 
0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "core.h"
0009 #ifdef CRASH_AUTO_TEST
0010 #include "logger.hpp"
0011 #endif
0012 #include "dialogs/splash.hpp"
0013 #include <config-kdenlive.h>
0014 
0015 #include <mlt++/Mlt.h>
0016 
0017 #include "kcoreaddons_version.h"
0018 #include "kxmlgui_version.h"
0019 #include "mainwindow.h"
0020 #include <project/projectmanager.h>
0021 
0022 #include <KAboutData>
0023 #include <KConfigGroup>
0024 #ifdef USE_DRMINGW
0025 #include <exchndl.h>
0026 #elif defined(KF5_USE_CRASH)
0027 #include <KCrash>
0028 #endif
0029 
0030 #include <KIconLoader>
0031 #include <KSharedConfig>
0032 
0033 #include "definitions.h"
0034 #include "kdenlive_debug.h"
0035 #ifndef NODBUS
0036 #include <KDBusService>
0037 #endif
0038 #include <KIconTheme>
0039 #include <KLocalizedString>
0040 #include <QApplication>
0041 #include <QCommandLineOption>
0042 #include <QCommandLineParser>
0043 #include <QDir>
0044 #include <QIcon>
0045 #include <QProcess>
0046 #include <QQmlEngine>
0047 #include <QQuickWindow>
0048 #include <QResource>
0049 #include <QSplashScreen>
0050 #include <QUrl> //new
0051 #include <kiconthemes_version.h>
0052 
0053 #include "render/renderrequest.h"
0054 
0055 // TODO: remove
0056 #include "doc/docundostack.hpp"
0057 #include "doc/kdenlivedoc.h"
0058 #include <QUndoGroup>
0059 
0060 #include "kdenlivesettings.h"
0061 #include <KNotification>
0062 
0063 #ifdef Q_OS_WIN
0064 extern "C" {
0065 // Inform the driver we could make use of the discrete gpu
0066 // __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
0067 // __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
0068 }
0069 #endif
0070 
0071 int main(int argc, char *argv[])
0072 {
0073     int result = EXIT_SUCCESS;
0074 #ifdef USE_DRMINGW
0075     ExcHndlInit();
0076 #endif
0077     // Force QDomDocument to use a deterministic XML attribute order
0078     qSetGlobalQHashSeed(0);
0079 
0080 #ifdef CRASH_AUTO_TEST
0081     Logger::init();
0082 #endif
0083 
0084 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0085     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0086     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0087 #endif
0088 
0089 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0090 #if defined(Q_OS_WIN)
0091     QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11);
0092 #elif defined(Q_OS_MACOS)
0093     QQuickWindow::setGraphicsApi(QSGRendererInterface::Metal);
0094 #else
0095     QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
0096     QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true);
0097 #endif
0098 #endif
0099 
0100 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0101     // blacklist MLT Qt5 module to prevent crashes
0102     qputenv("MLT_REPOSITORY_DENY", "libmltqt:libmltglaxnimate");
0103 #endif
0104 
0105 #if defined(Q_OS_WIN)
0106     QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
0107 #endif
0108     // TODO: is it a good option ?
0109     QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
0110 
0111     QApplication app(argc, argv);
0112 
0113     // Try to detect package type
0114     QString packageType;
0115     if (qEnvironmentVariableIsSet("PACKAGE_TYPE")) {
0116         packageType = qgetenv("PACKAGE_TYPE").toLower();
0117     } else {
0118         // no package type defined, try to detected it
0119         QString appPath = app.applicationDirPath();
0120         if (appPath.contains(QStringLiteral("/tmp/.mount_"))) {
0121             packageType = QStringLiteral("appimage");
0122         }
0123         if (appPath.contains(QStringLiteral("/snap"))) {
0124             packageType = QStringLiteral("snap");
0125         } else {
0126             qDebug() << "Could not detect package type, probably default? App dir is" << app.applicationDirPath();
0127         }
0128     }
0129 
0130     bool inSandbox = false;
0131     if (packageType == QStringLiteral("appimage") || packageType == QStringLiteral("flatpak") || packageType == QStringLiteral("snap")) {
0132         inSandbox = true;
0133         // use a dedicated config file for sandbox packages,
0134         // however the next line has no effect if the --config cmd option is used
0135         KConfig::setMainConfigName(QStringLiteral("kdenlive-%1rc").arg(packageType));
0136     }
0137 
0138     KLocalizedString::setApplicationDomain("kdenlive");
0139     qApp->processEvents(QEventLoop::AllEvents);
0140 
0141     // Create KAboutData
0142     QString otherText = i18n("Please report bugs to <a href=\"%1\">%2</a>", QStringLiteral("https://bugs.kde.org/enter_bug.cgi?product=kdenlive"),
0143                              QStringLiteral("https://bugs.kde.org/"));
0144     if (!packageType.isEmpty()) {
0145         otherText.prepend(i18n("You are using the %1 package.<br>", packageType));
0146     }
0147     KAboutData aboutData(QByteArray("kdenlive"), i18n("Kdenlive"), KDENLIVE_VERSION, i18n("An open source video editor."), KAboutLicense::GPL_V3,
0148                          i18n("Copyright © 2007–2024 Kdenlive authors"), otherText, QStringLiteral("https://kdenlive.org"));
0149     // main developers (alphabetical)
0150     aboutData.addAuthor(i18n("Jean-Baptiste Mardelle"), i18n("MLT and KDE SC 4 / KF5 port, main developer and maintainer"), QStringLiteral("jb@kdenlive.org"));
0151     // active developers with major involvement
0152     aboutData.addAuthor(i18n("Nicolas Carion"), i18n("Code re-architecture & timeline rewrite (2019)"), QStringLiteral("french.ebook.lover@gmail.com"));
0153     aboutData.addAuthor(i18n("Julius Künzel"), i18n("Feature development, packaging, bug fixing"), QStringLiteral("jk.kdedev@smartlab.uber.space"));
0154     aboutData.addAuthor(i18n("Vincent Pinon"), i18n("KF5 port, Windows cross-build, packaging, bug fixing"), QStringLiteral("vpinon@kde.org"));
0155     // other active developers (alphabetical)
0156     aboutData.addAuthor(i18n("Dan Dennedy"), i18n("MLT maintainer, Bug fixing, etc."), QStringLiteral("dan@dennedy.org"));
0157     aboutData.addAuthor(i18n("Simon A. Eugster"), i18n("Color scopes, bug fixing, etc."), QStringLiteral("simon.eu@gmail.com"));
0158     aboutData.addAuthor(i18n("Eric Jiang"), i18n("Bug fixing and test improvements"), QStringLiteral("jk.kdedev@smartlab.uber.space"));
0159     // non active developers with major improvement (alphabetical)
0160     aboutData.addAuthor(i18n("Jason Wood"), i18n("Original KDE 3 version author (not active anymore)"), QStringLiteral("jasonwood@blueyonder.co.uk"));
0161     // non developers (alphabetical)
0162     aboutData.addCredit(i18n("Farid Abdelnour"), i18n("Logo, Promotion, testing"));
0163     aboutData.addCredit(i18n("Eugen Mohr"), i18n("Bug triage, testing, documentation maintainer"));
0164     aboutData.addCredit(i18n("Nara Oliveira"), i18n("Logo"));
0165     aboutData.addCredit(i18n("Bruno Santos"), i18n("Testing"));
0166     aboutData.addCredit(i18n("Massimo Stella"), i18n("Expert advice, testing"));
0167 
0168     aboutData.setTranslator(i18n("NAME OF TRANSLATORS"), i18n("EMAIL OF TRANSLATORS"));
0169     aboutData.setOrganizationDomain(QByteArray("kde.org"));
0170 
0171     aboutData.addComponent(i18n("MLT"), i18n("Open source multimedia framework."), mlt_version_get_string(),
0172                            QStringLiteral("https://mltframework.org") /*, KAboutLicense::LGPL_V2_1*/);
0173     aboutData.addComponent(i18n("FFmpeg"), i18n("A complete, cross-platform solution to record, convert and stream audio and video."), QString(),
0174                            QStringLiteral("https://ffmpeg.org"));
0175 
0176     aboutData.setDesktopFileName(QStringLiteral("org.kde.kdenlive"));
0177 
0178     // Register about data
0179     KAboutData::setApplicationData(aboutData);
0180 
0181     // Set app stuff from about data
0182     app.setApplicationName(QStringLiteral("kdenlive"));
0183     app.setWindowIcon(QIcon(QStringLiteral(":/pics/kdenlive.png")));
0184     app.setApplicationDisplayName(aboutData.displayName());
0185     app.setOrganizationDomain(aboutData.organizationDomain());
0186     app.setApplicationVersion(aboutData.version());
0187     app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
0188 
0189     qApp->processEvents(QEventLoop::AllEvents);
0190 
0191     // Create command line parser with options
0192     QCommandLineParser parser;
0193     aboutData.setupCommandLine(&parser);
0194     parser.setApplicationDescription(aboutData.shortDescription());
0195 
0196     // config option is processed in KConfig (src/core/kconfig.cpp)
0197     parser.addOption(QCommandLineOption(QStringLiteral("config"), i18n("Set a custom config file name."), QStringLiteral("config file")));
0198     QCommandLineOption mltPathOption(QStringLiteral("mlt-path"), i18n("Set the path for MLT environment."), QStringLiteral("mlt-path"));
0199     parser.addOption(mltPathOption);
0200     QCommandLineOption mltLogLevelOption(QStringLiteral("mlt-log"), i18n("Set the MLT log level. Leave this unset for level \"warning\"."),
0201                                          QStringLiteral("verbose/debug"));
0202     parser.addOption(mltLogLevelOption);
0203     QCommandLineOption clipsOption(QStringLiteral("i"), i18n("Comma separated list of files to add as clips to the bin."), QStringLiteral("clips"));
0204     parser.addOption(clipsOption);
0205 
0206     // render options
0207     QCommandLineOption renderOption(QStringLiteral("render"), i18n("Directly render the project and exit."));
0208     parser.addOption(renderOption);
0209 
0210     QCommandLineOption presetOption(QStringLiteral("render-preset"), i18n("Kdenlive render preset name (MP4-H264/AAC will be used if none given)."),
0211                                     QStringLiteral("renderPreset"), QString());
0212     parser.addOption(presetOption);
0213 
0214     QCommandLineOption exitOption(QStringLiteral("render-async"),
0215                                   i18n("Exit after (detached) render process started, without this flag it exists only after it finished."));
0216     parser.addOption(exitOption);
0217 
0218     parser.addPositionalArgument(QStringLiteral("file"), i18n("Kdenlive document to open."));
0219     parser.addPositionalArgument(QStringLiteral("rendering"), i18n("Output file for rendered video."));
0220 
0221     // Parse command line
0222     parser.process(app);
0223     aboutData.processCommandLine(&parser);
0224 
0225     QUrl url;
0226     QUrl renderUrl;
0227     QString presetName;
0228     if (parser.positionalArguments().count() != 0) {
0229         const QString inputFilename = parser.positionalArguments().at(0);
0230         const QFileInfo fileInfo(inputFilename);
0231         url = QUrl(inputFilename);
0232         if (fileInfo.exists() || url.scheme().isEmpty()) { // easiest way to detect "invalid"/unintended URLs is no scheme
0233             url = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
0234         }
0235         if (parser.positionalArguments().count() > 1) {
0236             // Output render
0237             const QString outputFilename = parser.positionalArguments().at(1);
0238             const QFileInfo outFileInfo(outputFilename);
0239             if (!outFileInfo.exists()) { // easiest way to detect "invalid"/unintended URLs is no scheme
0240                 renderUrl = QUrl::fromLocalFile(outFileInfo.absoluteFilePath());
0241             }
0242         }
0243     }
0244 
0245     qApp->processEvents(QEventLoop::AllEvents);
0246 
0247     if (parser.isSet(renderOption)) {
0248         if (url.isEmpty()) {
0249             qCritical() << "You need to give a valid file if you want to render from the command line.";
0250             return EXIT_FAILURE;
0251         }
0252         if (renderUrl.isEmpty()) {
0253             qCritical() << "You need to give a non existing output file to render from the command line.";
0254             return EXIT_FAILURE;
0255         }
0256         if (parser.isSet(presetOption)) {
0257             presetName = parser.value(presetOption);
0258         } else {
0259             presetName = QStringLiteral("MP4-H264/AAC");
0260             qDebug() << "No render preset given, using default:" << presetName;
0261         }
0262         if (!Core::build(packageType, true)) {
0263             return EXIT_FAILURE;
0264         }
0265         pCore->initHeadless(url);
0266         app.processEvents();
0267 
0268         RenderRequest *renderrequest = new RenderRequest();
0269         renderrequest->setOutputFile(renderUrl.toLocalFile());
0270         renderrequest->loadPresetParams(presetName);
0271         // request->setPresetParams(m_params);
0272         renderrequest->setDelayedRendering(false);
0273         renderrequest->setProxyRendering(false);
0274         renderrequest->setEmbedSubtitles(false);
0275         renderrequest->setTwoPass(false);
0276         renderrequest->setAudioFilePerTrack(false);
0277 
0278         /*bool guideMultiExport = false;
0279         int guideCategory = m_view.guideCategoryChooser->currentCategory();
0280         renderrequest->setGuideParams(m_guidesModel, guideMultiExport, guideCategory);*/
0281 
0282         renderrequest->setOverlayData(QString());
0283         std::vector<RenderRequest::RenderJob> renderjobs = renderrequest->process();
0284         app.processEvents();
0285 
0286         if (!renderrequest->errorMessages().isEmpty()) {
0287             qInfo() << "The following errors occurred while trying to render:\n" << renderrequest->errorMessages().join(QLatin1Char('\n'));
0288         }
0289 
0290         int exitCode = EXIT_SUCCESS;
0291 
0292         for (const auto &job : renderjobs) {
0293             const QStringList argsJob = RenderRequest::argsByJob(job);
0294             qDebug() << "* CREATED JOB WITH ARGS: " << argsJob;
0295             qDebug() << "starting kdenlive_render process using: " << KdenliveSettings::kdenliverendererpath();
0296             if (!parser.isSet(exitOption)) {
0297                 if (QProcess::execute(KdenliveSettings::kdenliverendererpath(), argsJob) != EXIT_SUCCESS) {
0298                     exitCode = EXIT_FAILURE;
0299                     break;
0300                 }
0301             } else {
0302                 if (!QProcess::startDetached(KdenliveSettings::kdenliverendererpath(), argsJob)) {
0303                     qCritical() << "Error starting render job" << argsJob;
0304                     exitCode = EXIT_FAILURE;
0305                     break;
0306                 } else {
0307                     KNotification::event(QStringLiteral("RenderStarted"), i18n("Rendering <i>%1</i> started", job.outputPath), QPixmap());
0308                 }
0309             }
0310         }
0311         /*QMapIterator<QString, QString> i(rendermanager->m_renderFiles);
0312         while (i.hasNext()) {
0313             i.next();
0314             // qDebug() << i.key() << i.value() << rendermanager->startRendering(i.key(), i.value(), {});
0315         }*/
0316         pCore->projectManager()->closeCurrentDocument(false, false);
0317         app.processEvents();
0318         Core::clean();
0319         app.processEvents();
0320         return exitCode;
0321     }
0322 
0323 #if defined(Q_OS_WIN)
0324 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0325     KSharedConfigPtr configWin = KSharedConfig::openConfig("kdenliverc");
0326     KConfigGroup grp1(configWin, "misc");
0327     if (grp1.exists()) {
0328         int glMode = grp1.readEntry("opengl_backend", 0);
0329         if (glMode > 0) {
0330             QCoreApplication::setAttribute((Qt::ApplicationAttribute)glMode, true);
0331         }
0332     } else {
0333         // Default to OpenGLES (QtAngle) on first start
0334         QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true);
0335         grp1.writeEntry("opengl_backend", int(Qt::AA_UseOpenGLES));
0336     }
0337     configWin->sync();
0338 #endif
0339 #endif
0340 
0341     qApp->processEvents(QEventLoop::AllEvents);
0342     Splash splash;
0343     qApp->processEvents(QEventLoop::AllEvents);
0344     splash.showMessage(i18n("Version %1", QString(KDENLIVE_VERSION)), Qt::AlignRight | Qt::AlignBottom, Qt::white);
0345     splash.show();
0346     qApp->processEvents(QEventLoop::AllEvents);
0347 
0348 #ifdef Q_OS_WIN
0349     QString path = qApp->applicationDirPath() + QLatin1Char(';') + qgetenv("PATH");
0350     qputenv("PATH", path.toUtf8().constData());
0351 #endif
0352 
0353 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
0354     const QStringList themes{"/icons/breeze/breeze-icons.rcc", "/icons/breeze-dark/breeze-icons-dark.rcc"};
0355     for (const QString &theme : themes) {
0356         const QString themePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, theme);
0357         if (!themePath.isEmpty()) {
0358             const QString iconSubdir = theme.left(theme.lastIndexOf('/'));
0359             if (QResource::registerResource(themePath, iconSubdir)) {
0360                 if (QFileInfo::exists(QLatin1Char(':') + iconSubdir + QStringLiteral("/index.theme"))) {
0361                     qDebug() << "Loaded icon theme:" << theme;
0362                 } else {
0363                     qWarning() << "No index.theme found in" << theme;
0364                     QResource::unregisterResource(themePath, iconSubdir);
0365                 }
0366             } else {
0367                 qWarning() << "Invalid rcc file" << theme;
0368             }
0369         }
0370     }
0371 #else
0372     // AppImage
0373     if (packageType == QStringLiteral("appimage")) {
0374         QMap<QString, QString> themeMap;
0375         themeMap.insert("breeze", "/../icons/breeze/breeze-icons.rcc");
0376         themeMap.insert("breeze-dark", "/../icons/breeze-dark/breeze-icons-dark.rcc");
0377 
0378         QMapIterator<QString, QString> i(themeMap);
0379         while (i.hasNext()) {
0380             i.next();
0381             QString themePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, i.value());
0382             if (!themePath.isEmpty()) {
0383                 const QString iconSubdir = "/icons/" + i.key();
0384                 if (QResource::registerResource(themePath, iconSubdir)) {
0385                     if (QFileInfo::exists(QLatin1Char(':') + iconSubdir + QStringLiteral("/index.theme"))) {
0386                         qDebug() << "Loaded icon theme:" << i.key();
0387                     } else {
0388                         qWarning() << "No index.theme found for" << i.key();
0389                         QResource::unregisterResource(themePath, iconSubdir);
0390                     }
0391                 } else {
0392                     qWarning() << "Invalid rcc file" << i.key();
0393                 }
0394             }
0395         }
0396     }
0397 #endif
0398 
0399     KSharedConfigPtr config = KSharedConfig::openConfig();
0400     KConfigGroup grp(config, "unmanaged");
0401     if (!grp.exists()) {
0402         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0403         if (env.value(QStringLiteral("XDG_CURRENT_DESKTOP")).toLower() == QLatin1String("kde") && packageType != QStringLiteral("appimage")) {
0404             qCDebug(KDENLIVE_LOG) << "KDE Desktop detected and not Appimage, using system icons";
0405         } else {
0406             // We are not on a KDE desktop or in an Appimage, force breeze icon theme
0407             // Check if breeze theme is available
0408             QStringList iconThemes = KIconTheme::list();
0409             if (iconThemes.contains(QStringLiteral("breeze"))) {
0410                 grp.writeEntry("force_breeze", true);
0411                 grp.writeEntry("use_dark_breeze", true);
0412                 qCDebug(KDENLIVE_LOG) << "Non KDE Desktop or Appimage detected, forcing Breeze icon theme";
0413             }
0414         }
0415     }
0416     KConfigGroup uicg(config, "UiSettings");
0417     if (!uicg.exists()) {
0418         uicg.writeEntry("ColorSchemePath", "BreezeDark.colors");
0419         uicg.sync();
0420     }
0421 
0422 #ifndef NODBUS
0423     // Init DBus services
0424     KDBusService programDBusService;
0425 #endif
0426     bool forceBreeze = grp.readEntry("force_breeze", QVariant(false)).toBool();
0427     if (forceBreeze) {
0428         bool darkBreeze = grp.readEntry("use_dark_breeze", QVariant(false)).toBool();
0429         QIcon::setThemeName(darkBreeze ? QStringLiteral("breeze-dark") : QStringLiteral("breeze"));
0430     }
0431     qApp->processEvents(QEventLoop::AllEvents);
0432 
0433 #ifdef USE_DRMINGW
0434     ExcHndlInit();
0435 #elif defined(KF5_USE_CRASH)
0436     KCrash::initialize();
0437 #endif
0438 
0439     qmlRegisterUncreatableMetaObject(PlaylistState::staticMetaObject, // static meta object
0440                                      "com.enums",                     // import statement
0441                                      1, 0,                            // major and minor version of the import
0442                                      "ClipState",                     // name in QML
0443                                      "Error: only enums");
0444     qmlRegisterUncreatableMetaObject(FileStatus::staticMetaObject, // static meta object
0445                                      "com.enums",                  // import statement
0446                                      1, 0,                         // major and minor version of the import
0447                                      "ClipStatus",                 // name in QML
0448                                      "Error: only enums");
0449     qmlRegisterUncreatableMetaObject(ClipType::staticMetaObject, // static meta object
0450                                      "com.enums",                // import statement
0451                                      1, 0,                       // major and minor version of the import
0452                                      "ProducerType",             // name in QML
0453                                      "Error: only enums");
0454     qmlRegisterUncreatableMetaObject(AssetListType::staticMetaObject, // static meta object
0455                                      "com.enums",                     // import statement
0456                                      1, 0,                            // major and minor version of the import
0457                                      "AssetType",                     // name in QML
0458                                      "Error: only enums");
0459     qmlRegisterUncreatableMetaObject(ToolType::staticMetaObject, // static meta object
0460                                      "com.enums",                // import statement
0461                                      1, 0,                       // major and minor version of the import
0462                                      "ProjectTool",              // name in QML
0463                                      "Error: only enums");
0464     if (parser.value(mltLogLevelOption) == QStringLiteral("verbose")) {
0465         mlt_log_set_level(MLT_LOG_VERBOSE);
0466     } else if (parser.value(mltLogLevelOption) == QStringLiteral("debug")) {
0467         mlt_log_set_level(MLT_LOG_DEBUG);
0468     }
0469     const QString clipsToLoad = parser.value(clipsOption);
0470     qApp->processEvents(QEventLoop::AllEvents);
0471     if (!Core::build(packageType)) {
0472         // App is crashing, delete config files and restart
0473         result = EXIT_CLEAN_RESTART;
0474     } else {
0475         QObject::connect(pCore.get(), &Core::loadingMessageNewStage, &splash, &Splash::showProgressMessage, Qt::DirectConnection);
0476         QObject::connect(pCore.get(), &Core::loadingMessageIncrease, &splash, &Splash::increaseProgressMessage, Qt::DirectConnection);
0477         QObject::connect(pCore.get(), &Core::loadingMessageHide, &splash, &Splash::clearMessage, Qt::DirectConnection);
0478         QObject::connect(pCore.get(), &Core::closeSplash, &splash, [&]() { splash.finish(pCore->window()); });
0479         pCore->initGUI(inSandbox, parser.value(mltPathOption), url, clipsToLoad);
0480         result = app.exec();
0481     }
0482     Core::clean();
0483     if (result == EXIT_RESTART || result == EXIT_CLEAN_RESTART) {
0484         qCDebug(KDENLIVE_LOG) << "restarting app";
0485         if (result == EXIT_CLEAN_RESTART) {
0486             // Delete config file
0487             KSharedConfigPtr config = KSharedConfig::openConfig();
0488             if (config->name().contains(QLatin1String("kdenlive"))) {
0489                 // Make sure we delete our config file
0490                 QFile f(QStandardPaths::locate(QStandardPaths::GenericConfigLocation, config->name(), QStandardPaths::LocateFile));
0491                 if (f.exists()) {
0492                     qDebug() << " = = = =\nGOT Deleted file: " << f.fileName();
0493                     f.remove();
0494                 }
0495             }
0496             // Delete xml ui rc file
0497             const QString configLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
0498             if (!configLocation.isEmpty()) {
0499                 QDir dir(configLocation);
0500                 if (dir.cd(QStringLiteral("kxmlgui5")) && dir.cd(QStringLiteral("kdenlive"))) {
0501                     QFile f(dir.absoluteFilePath(QStringLiteral("kdenliveui.rc")));
0502                     if (f.exists() && f.open(QIODevice::ReadOnly)) {
0503                         bool shortcutFound = false;
0504                         QDomDocument doc;
0505                         doc.setContent(&f);
0506                         f.close();
0507                         if (!doc.documentElement().isNull()) {
0508                             QDomElement shortcuts = doc.documentElement().firstChildElement(QStringLiteral("ActionProperties"));
0509                             if (!shortcuts.isNull()) {
0510                                 qDebug() << "==== FOUND CUSTOM SHORTCUTS!!!";
0511                                 // Copy the original settings and append custom shortcuts
0512                                 QFile f2(QStringLiteral(":/kxmlgui5/kdenlive/kdenliveui.rc"));
0513                                 if (f2.exists() && f2.open(QIODevice::ReadOnly)) {
0514                                     QDomDocument doc2;
0515                                     doc2.setContent(&f2);
0516                                     f2.close();
0517                                     if (!doc2.documentElement().isNull()) {
0518                                         doc2.documentElement().appendChild(doc2.importNode(shortcuts, true));
0519                                         shortcutFound = true;
0520                                         if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
0521                                             // overwrite local xml config
0522                                             QTextStream out(&f);
0523 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0524                                             out.setCodec("UTF-8");
0525 #endif
0526                                             out << doc2.toString();
0527                                             f.close();
0528                                         }
0529                                     }
0530                                 }
0531                             }
0532                         }
0533                         if (!shortcutFound) {
0534                             // No custom shortcuts found, simply delete the xmlui file
0535                             f.remove();
0536                         }
0537                     }
0538                 }
0539             }
0540         }
0541         QStringList progArgs;
0542         if (argc > 1) {
0543             // Start at 1 to remove app name
0544             for (int i = 1; i < argc; i++) {
0545                 progArgs << QString(argv[i]);
0546             }
0547         }
0548         auto *restart = new QProcess;
0549         restart->start(app.applicationFilePath(), progArgs);
0550         restart->waitForReadyRead();
0551         restart->waitForFinished(1000);
0552         result = EXIT_SUCCESS;
0553     }
0554     return result;
0555 }