File indexing completed on 2022-10-04 15:37:20

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 
0021 #include <KAboutData>
0022 #include <KConfigGroup>
0023 #ifdef USE_DRMINGW
0024 #include <exchndl.h>
0025 #elif defined(KF5_USE_CRASH)
0026 #include <KCrash>
0027 #endif
0028 
0029 #include <KIconLoader>
0030 #include <KSharedConfig>
0031 
0032 #include "definitions.h"
0033 #include "kdenlive_debug.h"
0034 #ifndef NODBUS
0035 #include <KDBusService>
0036 #endif
0037 #include <KIconTheme>
0038 #include <QApplication>
0039 #include <QCommandLineOption>
0040 #include <QCommandLineParser>
0041 #include <QQuickWindow>
0042 #include <QDir>
0043 #include <QIcon>
0044 #include <QProcess>
0045 #include <QQmlEngine>
0046 #include <QResource>
0047 #include <QSplashScreen>
0048 #include <QUrl> //new
0049 #include <kiconthemes_version.h>
0050 #include <klocalizedstring.h>
0051 
0052 #ifdef Q_OS_WIN
0053 extern "C" {
0054 // Inform the driver we could make use of the discrete gpu
0055 // __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
0056 // __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
0057 }
0058 #endif
0059 
0060 int main(int argc, char *argv[])
0061 {
0062 #ifdef USE_DRMINGW
0063     ExcHndlInit();
0064 #endif
0065     // Force QDomDocument to use a deterministic XML attribute order
0066     qSetGlobalQHashSeed(0);
0067 
0068 #ifdef CRASH_AUTO_TEST
0069     Logger::init();
0070 #endif
0071 
0072 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0073     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0074     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0075 #endif
0076 
0077 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0078     QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
0079 #endif
0080 
0081 #if defined(Q_OS_WIN)
0082     QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
0083 #endif
0084 
0085     // TODO: is it a good option ?
0086     QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
0087 
0088 #if defined(Q_OS_WIN)
0089     KSharedConfigPtr configWin = KSharedConfig::openConfig("kdenliverc");
0090     KConfigGroup grp1(configWin, "misc");
0091     if (grp1.exists()) {
0092         int glMode = grp1.readEntry("opengl_backend", 0);
0093         if (glMode > 0) {
0094             QCoreApplication::setAttribute((Qt::ApplicationAttribute)glMode, true);
0095         }
0096     } else {
0097         // Default to OpenGLES (QtAngle) on first start
0098         QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true);
0099         grp1.writeEntry("opengl_backend", int(Qt::AA_UseOpenGLES));
0100     }
0101     configWin->sync();
0102 #endif
0103     QApplication app(argc, argv);
0104     app.setApplicationName(QStringLiteral("kdenlive"));
0105     app.setOrganizationDomain(QStringLiteral("kde.org"));
0106     app.setWindowIcon(QIcon(QStringLiteral(":/pics/kdenlive.png")));
0107     KLocalizedString::setApplicationDomain("kdenlive");
0108 
0109     qApp->processEvents(QEventLoop::AllEvents);
0110     Splash splash;
0111     qApp->processEvents(QEventLoop::AllEvents);
0112     splash.showMessage(i18n("Version %1", QString(KDENLIVE_VERSION)), Qt::AlignRight | Qt::AlignBottom, Qt::white);
0113     splash.show();
0114     qApp->processEvents(QEventLoop::AllEvents);
0115 
0116     QString packageType;
0117     if (qEnvironmentVariableIsSet("PACKAGE_TYPE")) {
0118         packageType = qgetenv("PACKAGE_TYPE").toLower();
0119     } else {
0120         // no package type defined, try to detected it
0121         QString appPath = qApp->applicationDirPath();
0122         if (appPath.contains(QStringLiteral("/tmp/.mount_"))) {
0123             packageType = QStringLiteral("appimage");
0124         }
0125         if (appPath.contains(QStringLiteral("/snap"))) {
0126             packageType = QStringLiteral("snap");
0127         } else {
0128             qDebug() << "Could not detect package type, probably default? App dir is" << qApp->applicationDirPath();
0129         }
0130     }
0131 
0132 #ifdef Q_OS_WIN
0133     qputenv("KDE_FORK_SLAVES", "1");
0134     QString path = qApp->applicationDirPath() + QLatin1Char(';') + qgetenv("PATH");
0135     qputenv("PATH", path.toUtf8().constData());
0136 #endif
0137 
0138 #if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
0139     const QStringList themes{"/icons/breeze/breeze-icons.rcc", "/icons/breeze-dark/breeze-icons-dark.rcc"};
0140     for (const QString &theme : themes) {
0141         const QString themePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, theme);
0142         if (!themePath.isEmpty()) {
0143             const QString iconSubdir = theme.left(theme.lastIndexOf('/'));
0144             if (QResource::registerResource(themePath, iconSubdir)) {
0145                 if (QFileInfo::exists(QLatin1Char(':') + iconSubdir + QStringLiteral("/index.theme"))) {
0146                     qDebug() << "Loaded icon theme:" << theme;
0147                 } else {
0148                     qWarning() << "No index.theme found in" << theme;
0149                     QResource::unregisterResource(themePath, iconSubdir);
0150                 }
0151             } else {
0152                 qWarning() << "Invalid rcc file" << theme;
0153             }
0154         }
0155     }
0156 #else
0157     // AppImage
0158     if (packageType == QStringLiteral("appimage")) {
0159         QMap<QString, QString> themeMap;
0160         themeMap.insert("breeze", "/../icons/breeze/breeze-icons.rcc");
0161         themeMap.insert("breeze-dark", "/../icons/breeze-dark/breeze-icons-dark.rcc");
0162 
0163         QMapIterator<QString, QString> i(themeMap);
0164         while (i.hasNext()) {
0165             i.next();
0166             QString themePath = QStandardPaths::locate(QStandardPaths::AppDataLocation, i.value());
0167             if (!themePath.isEmpty()) {
0168                 const QString iconSubdir = "/icons/" + i.key();
0169                 if (QResource::registerResource(themePath, iconSubdir)) {
0170                     if (QFileInfo::exists(QLatin1Char(':') + iconSubdir + QStringLiteral("/index.theme"))) {
0171                         qDebug() << "Loaded icon theme:" << i.key();
0172                     } else {
0173                         qWarning() << "No index.theme found for" << i.key();
0174                         QResource::unregisterResource(themePath, iconSubdir);
0175                     }
0176                 } else {
0177                     qWarning() << "Invalid rcc file" << i.key();
0178                 }
0179             }
0180         }
0181     }
0182 #endif
0183 
0184     bool inSandbox = false;
0185     if (packageType == QStringLiteral("appimage") || packageType == QStringLiteral("flatpak") || packageType == QStringLiteral("snap")) {
0186         inSandbox = true;
0187         // use a dedicated config file for sandbox packages,
0188         // however the next line has no effect if the --config cmd option is used
0189         KConfig::setMainConfigName(QStringLiteral("kdenlive-%1rc").arg(packageType));
0190     }
0191 
0192     KSharedConfigPtr config = KSharedConfig::openConfig();
0193     KConfigGroup grp(config, "unmanaged");
0194     if (!grp.exists()) {
0195         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0196         if (env.value(QStringLiteral("XDG_CURRENT_DESKTOP")).toLower() == QLatin1String("kde") && packageType != QStringLiteral("appimage")) {
0197             qCDebug(KDENLIVE_LOG) << "KDE Desktop detected and not Appimage, using system icons";
0198         } else {
0199             // We are not on a KDE desktop or in an Appimage, force breeze icon theme
0200             // Check if breeze theme is available
0201             QStringList iconThemes = KIconTheme::list();
0202             if (iconThemes.contains(QStringLiteral("breeze"))) {
0203                 grp.writeEntry("force_breeze", true);
0204                 grp.writeEntry("use_dark_breeze", true);
0205                 qCDebug(KDENLIVE_LOG) << "Non KDE Desktop or Appimage detected, forcing Breeze icon theme";
0206             }
0207         }
0208     }
0209     KConfigGroup uicg(config, "UiSettings");
0210     if (!uicg.exists()) {
0211         uicg.writeEntry("ColorSchemePath", "BreezeDark.colors");
0212         uicg.sync();
0213     }
0214 
0215 #ifndef NODBUS
0216     // Init DBus services
0217     KDBusService programDBusService;
0218 #endif
0219     bool forceBreeze = grp.readEntry("force_breeze", QVariant(false)).toBool();
0220     if (forceBreeze) {
0221         bool darkBreeze = grp.readEntry("use_dark_breeze", QVariant(false)).toBool();
0222         QIcon::setThemeName(darkBreeze ? QStringLiteral("breeze-dark") : QStringLiteral("breeze"));
0223     }
0224     qApp->processEvents(QEventLoop::AllEvents);
0225 
0226     // Create KAboutData
0227     QString otherText = i18n("Please report bugs to <a href=\"%1\">%2</a>", QStringLiteral("https://bugs.kde.org/enter_bug.cgi?product=kdenlive"),
0228                              QStringLiteral("https://bugs.kde.org/"));
0229     if (!packageType.isEmpty()) {
0230         otherText.prepend(i18n("You are using the %1 package.<br>", packageType));
0231     }
0232     KAboutData aboutData(QByteArray("kdenlive"), i18n("Kdenlive"), KDENLIVE_VERSION, i18n("An open source video editor."), KAboutLicense::GPL_V3,
0233                          i18n("Copyright © 2007–2022 Kdenlive authors"), otherText, QStringLiteral("https://kdenlive.org"));
0234     // main developers (alphabetical)
0235     aboutData.addAuthor(i18n("Jean-Baptiste Mardelle"), i18n("MLT and KDE SC 4 / KF5 port, main developer and maintainer"), QStringLiteral("jb@kdenlive.org"));
0236     // active developers with major involvement
0237     aboutData.addAuthor(i18n("Nicolas Carion"), i18n("Code re-architecture & timeline rewrite"), QStringLiteral("french.ebook.lover@gmail.com"));
0238     aboutData.addAuthor(i18n("Simon A. Eugster"), i18n("Color scopes, bug fixing, etc."), QStringLiteral("simon.eu@gmail.com"));
0239     aboutData.addAuthor(i18n("Vincent Pinon"), i18n("KF5 port, Windows cross-build, packaging, bug fixing"), QStringLiteral("vpinon@kde.org"));
0240     // other active developers (alphabetical)
0241     aboutData.addAuthor(i18n("Dan Dennedy"), i18n("MLT, Bug fixing, etc."), QStringLiteral("dan@dennedy.org"));
0242     aboutData.addAuthor(i18n("Julius Künzel"), i18n("Bug fixing, etc."), QStringLiteral("jk.kdedev@smartlab.uber.space"));
0243     aboutData.addAuthor(i18n("Sashmita Raghav"), i18n("Subtitle feature (GSoC), timeline colours"));
0244     // non active developers with major improvement (alphabetical)
0245     aboutData.addAuthor(i18n("Jason Wood"), i18n("Original KDE 3 version author (not active anymore)"), QStringLiteral("jasonwood@blueyonder.co.uk"));
0246     // non developers (alphabetical)
0247     aboutData.addCredit(i18n("Farid Abdelnour"), i18n("Logo, Promotion, testing"));
0248     aboutData.addCredit(i18n("Eugen Mohr"), i18n("Bug triage, testing"));
0249     aboutData.addCredit(i18n("Nara Oliveira"), i18n("Logo"));
0250     aboutData.addCredit(i18n("Bruno Santos"), i18n("Testing"));
0251     aboutData.addCredit(i18n("Massimo Stella"), i18n("Expert advice, testing"));
0252 
0253     aboutData.setTranslator(i18n("NAME OF TRANSLATORS"), i18n("EMAIL OF TRANSLATORS"));
0254     aboutData.setOrganizationDomain(QByteArray("kde.org"));
0255 #if KXMLGUI_VERSION < QT_VERSION_CHECK(5, 87, 0)
0256     aboutData.setOtherText(
0257         i18n("Using:\n<a href=\"https://mltframework.org\">MLT</a> version %1\n<a href=\"https://ffmpeg.org\">FFmpeg</a> libraries", mlt_version_get_string()));
0258 #endif
0259 
0260     aboutData.addComponent(i18n("MLT"), i18n("Open source multimedia framework."), mlt_version_get_string(),
0261                            QStringLiteral("https://mltframework.org") /*, KAboutLicense::LGPL_V2_1*/);
0262     aboutData.addComponent(i18n("FFmpeg"), i18n("A complete, cross-platform solution to record, convert and stream audio and video."), QString(),
0263                            QStringLiteral("https://ffmpeg.org"));
0264 
0265     aboutData.setDesktopFileName(QStringLiteral("org.kde.kdenlive"));
0266 
0267     // Register about data
0268     KAboutData::setApplicationData(aboutData);
0269 
0270     // Set app stuff from about data
0271     app.setApplicationDisplayName(aboutData.displayName());
0272     app.setOrganizationDomain(aboutData.organizationDomain());
0273     app.setApplicationVersion(aboutData.version());
0274     app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
0275     qApp->processEvents(QEventLoop::AllEvents);
0276 
0277     // Create command line parser with options
0278     QCommandLineParser parser;
0279     aboutData.setupCommandLine(&parser);
0280     parser.setApplicationDescription(aboutData.shortDescription());
0281 
0282     // config option is processed in KConfig (src/core/kconfig.cpp)
0283     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("config"), i18n("Set a custom config file name"), QStringLiteral("config")));
0284     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("mlt-path"), i18n("Set the path for MLT environment"), QStringLiteral("mlt-path")));
0285     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("mlt-log"), i18n("MLT log level"), QStringLiteral("verbose/debug")));
0286     parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("i"), i18n("Comma separated list of clips to add"), QStringLiteral("clips")));
0287     parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open"));
0288 
0289     // Parse command line
0290     parser.process(app);
0291     aboutData.processCommandLine(&parser);
0292 
0293     qApp->processEvents(QEventLoop::AllEvents);
0294 
0295 #ifdef USE_DRMINGW
0296     ExcHndlInit();
0297 #elif defined(KF5_USE_CRASH)
0298     KCrash::initialize();
0299 #endif
0300 
0301     qmlRegisterUncreatableMetaObject(PlaylistState::staticMetaObject, // static meta object
0302                                      "com.enums",                     // import statement
0303                                      1, 0,                            // major and minor version of the import
0304                                      "ClipState",                     // name in QML
0305                                      "Error: only enums");
0306     qmlRegisterUncreatableMetaObject(FileStatus::staticMetaObject, // static meta object
0307                                      "com.enums",                  // import statement
0308                                      1, 0,                         // major and minor version of the import
0309                                      "ClipStatus",                 // name in QML
0310                                      "Error: only enums");
0311     qmlRegisterUncreatableMetaObject(ClipType::staticMetaObject, // static meta object
0312                                      "com.enums",                // import statement
0313                                      1, 0,                       // major and minor version of the import
0314                                      "ProducerType",             // name in QML
0315                                      "Error: only enums");
0316     qmlRegisterUncreatableMetaObject(AssetListType::staticMetaObject, // static meta object
0317                                      "com.enums",                     // import statement
0318                                      1, 0,                            // major and minor version of the import
0319                                      "AssetType",                     // name in QML
0320                                      "Error: only enums");
0321     qmlRegisterUncreatableMetaObject(ToolType::staticMetaObject, // static meta object
0322                                      "com.enums",                // import statement
0323                                      1, 0,                       // major and minor version of the import
0324                                      "ProjectTool",              // name in QML
0325                                      "Error: only enums");
0326     if (parser.value(QStringLiteral("mlt-log")) == QStringLiteral("verbose")) {
0327         mlt_log_set_level(MLT_LOG_VERBOSE);
0328     } else if (parser.value(QStringLiteral("mlt-log")) == QStringLiteral("debug")) {
0329         mlt_log_set_level(MLT_LOG_DEBUG);
0330     }
0331     const QString clipsToLoad = parser.value(QStringLiteral("i"));
0332     QUrl url;
0333     if (parser.positionalArguments().count() != 0) {
0334         const QString inputFilename = parser.positionalArguments().at(0);
0335         const QFileInfo fileInfo(inputFilename);
0336         url = QUrl(inputFilename);
0337         if (fileInfo.exists() || url.scheme().isEmpty()) { // easiest way to detect "invalid"/unintended URLs is no scheme
0338             url = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
0339         }
0340     }
0341     qApp->processEvents(QEventLoop::AllEvents);
0342     int result = 0;
0343     if (!Core::build(packageType)) {
0344         // App is crashing, delete config files and restart
0345         result = EXIT_CLEAN_RESTART;
0346     } else {
0347         QObject::connect(pCore.get(), &Core::loadingMessageUpdated, &splash, &Splash::showProgressMessage, Qt::DirectConnection);
0348         QObject::connect(pCore.get(), &Core::closeSplash, &splash, [&]() { splash.finish(pCore->window()); });
0349         pCore->initGUI(inSandbox, parser.value(QStringLiteral("mlt-path")), url, clipsToLoad);
0350         result = app.exec();
0351     }
0352     Core::clean();
0353     if (result == EXIT_RESTART || result == EXIT_CLEAN_RESTART) {
0354         qCDebug(KDENLIVE_LOG) << "restarting app";
0355         if (result == EXIT_CLEAN_RESTART) {
0356             // Delete config file
0357             KSharedConfigPtr config = KSharedConfig::openConfig();
0358             if (config->name().contains(QLatin1String("kdenlive"))) {
0359                 // Make sure we delete our config file
0360                 QFile f(QStandardPaths::locate(QStandardPaths::GenericConfigLocation, config->name(), QStandardPaths::LocateFile));
0361                 if (f.exists()) {
0362                     qDebug() << " = = = =\nGOT Deleted file: " << f.fileName();
0363                     f.remove();
0364                 }
0365             }
0366             // Delete xml ui rc file
0367             QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kxmlgui5"), QStandardPaths::LocateDirectory));
0368             if (dir.exists()) {
0369                 dir.cd(QStringLiteral("kdenlive"));
0370             }
0371             if (dir.exists()) {
0372                 QFile f(dir.absoluteFilePath(QStringLiteral("kdenliveui.rc")));
0373                 if (f.exists()) {
0374                     qDebug() << " = = = =\nGOT Deleted file: " << f.fileName();
0375                     f.remove();
0376                 }
0377             }
0378         }
0379         QStringList progArgs;
0380         if (argc > 1) {
0381             // Start at 1 to remove app name
0382             for (int i = 1; i < argc; i++) {
0383                 progArgs << QString(argv[i]);
0384             }
0385         }
0386         auto *restart = new QProcess;
0387         restart->start(app.applicationFilePath(), progArgs);
0388         restart->waitForReadyRead();
0389         restart->waitForFinished(1000);
0390         result = EXIT_SUCCESS;
0391     }
0392     return result;
0393 }