main.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #include <locale.h>
  2. #include <QGuiApplication>
  3. #include <QApplication>
  4. #include <QFileInfo>
  5. #include <QIcon>
  6. #include <QtQml>
  7. #include <QtWebEngine/qtwebengineglobal.h>
  8. #include <QErrorMessage>
  9. #include <QCommandLineOption>
  10. #include "shared/Names.h"
  11. #include "system/SystemComponent.h"
  12. #include "system/UpdateManager.h"
  13. #include "system/UpdaterComponent.h"
  14. #include "QsLog.h"
  15. #include "Paths.h"
  16. #include "player/CodecsComponent.h"
  17. #include "player/PlayerComponent.h"
  18. #include "player/OpenGLDetect.h"
  19. #include "breakpad/CrashDumps.h"
  20. #include "Version.h"
  21. #include "settings/SettingsComponent.h"
  22. #include "settings/SettingsSection.h"
  23. #include "ui/KonvergoWindow.h"
  24. #include "ui/KonvergoWindow.h"
  25. #include "Globals.h"
  26. #include "ui/ErrorMessage.h"
  27. #include "UniqueApplication.h"
  28. #include "utils/HelperLauncher.h"
  29. #include "utils/Log.h"
  30. #ifdef Q_OS_MAC
  31. #include "PFMoveApplication.h"
  32. #endif
  33. #if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
  34. #include "SignalManager.h"
  35. #endif
  36. /////////////////////////////////////////////////////////////////////////////////////////
  37. static void preinitQt()
  38. {
  39. QCoreApplication::setApplicationName(Names::MainName());
  40. QCoreApplication::setApplicationVersion(Version::GetVersionString());
  41. QCoreApplication::setOrganizationDomain("plex.tv");
  42. #ifdef Q_OS_WIN32
  43. QVariant useOpengl = SettingsComponent::readPreinitValue(SETTINGS_SECTION_MAIN, "useOpenGL");
  44. // Warning: this must be the same as the default value as declared in
  45. // the settings_description.json file, or confusion will result.
  46. if (useOpengl.type() != QMetaType::Bool)
  47. useOpengl = false;
  48. if (useOpengl.toBool())
  49. QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
  50. else
  51. QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
  52. #endif
  53. }
  54. /////////////////////////////////////////////////////////////////////////////////////////
  55. char** appendCommandLineArguments(int argc, char **argv, const QStringList& args)
  56. {
  57. size_t newSize = (argc + args.length() + 1) * sizeof(char*);
  58. char** newArgv = (char**)calloc(1, newSize);
  59. memcpy(newArgv, argv, (size_t)(argc * sizeof(char*)));
  60. int pos = argc;
  61. for(const QString& str : args)
  62. newArgv[pos++] = qstrdup(str.toUtf8().data());
  63. return newArgv;
  64. }
  65. /////////////////////////////////////////////////////////////////////////////////////////
  66. void ShowLicenseInfo()
  67. {
  68. QFile licenses(":/misc/licenses.txt");
  69. licenses.open(QIODevice::ReadOnly | QIODevice::Text);
  70. QByteArray contents = licenses.readAll();
  71. printf("%.*s\n", contents.size(), contents.data());
  72. }
  73. /////////////////////////////////////////////////////////////////////////////////////////
  74. QStringList g_qtFlags = {
  75. "--disable-gpu",
  76. "--disable-web-security"
  77. };
  78. /////////////////////////////////////////////////////////////////////////////////////////
  79. int main(int argc, char *argv[])
  80. {
  81. try
  82. {
  83. QCommandLineParser parser;
  84. parser.setApplicationDescription("Plex Media Player");
  85. parser.addHelpOption();
  86. parser.addVersionOption();
  87. parser.addOptions({{{"l", "licenses"}, "Show license information"},
  88. {{"a", "from-auto-update"}, "When invoked from auto-update"},
  89. {"desktop", "Start in desktop mode"},
  90. {"tv", "Start in TV mode"},
  91. {"auto-layout", "Use auto-layout mode"},
  92. {"windowed", "Start in windowed mode"},
  93. {"fullscreen", "Start in fullscreen"},
  94. {"no-updates", "Disable auto-updating"},
  95. {"terminal", "Log to terminal"}});
  96. auto scaleOption = QCommandLineOption("scale-factor", "Set to a integer or default auto which controls" \
  97. "the scale (DPI) of the desktop interface.");
  98. scaleOption.setValueName("scale");
  99. scaleOption.setDefaultValue("auto");
  100. parser.addOption(scaleOption);
  101. char **newArgv = appendCommandLineArguments(argc, argv, g_qtFlags);
  102. int newArgc = argc + g_qtFlags.size();
  103. // Qt calls setlocale(LC_ALL, "") in a bunch of places, which breaks
  104. // float/string processing in mpv and ffmpeg.
  105. #ifdef Q_OS_UNIX
  106. qputenv("LC_ALL", "C");
  107. qputenv("LC_NUMERIC", "C");
  108. #endif
  109. preinitQt();
  110. detectOpenGLEarly();
  111. QStringList arguments;
  112. for (int i = 0; i < argc; i++)
  113. arguments << QString::fromLatin1(argv[i]);
  114. {
  115. // This is kinda dumb. But in order for the QCommandLineParser
  116. // to work properly we need to init if before we call process
  117. // but we don't want to do that for the main application since
  118. // we need to set the scale factor before we do that. So it becomes
  119. // a small chicken-or-egg problem, which we "solve" by making
  120. // this temporary console app.
  121. //
  122. QCoreApplication core(newArgc, newArgv);
  123. // Now parse the command line.
  124. parser.process(arguments);
  125. }
  126. if (parser.isSet("licenses"))
  127. {
  128. ShowLicenseInfo();
  129. return EXIT_SUCCESS;
  130. }
  131. auto scale = parser.value("scale-factor");
  132. if (scale.isEmpty() || scale == "auto")
  133. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  134. else if (scale != "none")
  135. qputenv("QT_SCALE_FACTOR", scale.toUtf8());
  136. QApplication app(newArgc, newArgv);
  137. #ifdef Q_OS_WIN
  138. // Setting the icon on Windows is necessary but will break user
  139. // ability to change icon on OSX
  140. app.setWindowIcon(QIcon(":/images/icon.png"));
  141. #endif
  142. #if defined(Q_OS_MAC) && defined(NDEBUG)
  143. PFMoveToApplicationsFolderIfNecessary();
  144. #endif
  145. // init breakpad.
  146. setupCrashDumper();
  147. UniqueApplication* uniqueApp = new UniqueApplication();
  148. if (!uniqueApp->ensureUnique())
  149. return EXIT_SUCCESS;
  150. #ifdef Q_OS_UNIX
  151. // install signals handlers for proper app closing.
  152. SignalManager signalManager(&app);
  153. Q_UNUSED(signalManager);
  154. #endif
  155. Log::Init();
  156. if (parser.isSet("terminal"))
  157. Log::EnableTerminalOutput();
  158. // Quit app and apply update if we find one.
  159. if (!parser.isSet("no-updates") && UpdateManager::CheckForUpdates())
  160. {
  161. app.quit();
  162. return 0;
  163. }
  164. detectOpenGLLate();
  165. Codecs::preinitCodecs();
  166. // Initialize all the components. This needs to be done
  167. // early since most everything else relies on it
  168. //
  169. ComponentManager::Get().initialize();
  170. if (parser.isSet("no-updates"))
  171. UpdaterComponent::Get().disable();
  172. SettingsComponent::Get().setCommandLineValues(parser.optionNames());
  173. // enable remote inspection if we have the correct setting for it.
  174. if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "remoteInspector").toBool())
  175. qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "0.0.0.0:9992");
  176. QtWebEngine::initialize();
  177. // start our helper
  178. #if ENABLE_HELPER
  179. HelperLauncher::Get().connectToHelper();
  180. #endif
  181. // load QtWebChannel so that we can register our components with it.
  182. QQmlApplicationEngine *engine = Globals::Engine();
  183. KonvergoWindow::RegisterClass();
  184. Globals::SetContextProperty("components", &ComponentManager::Get().getQmlPropertyMap());
  185. // the only way to detect if QML parsing fails is to hook to this signal and then see
  186. // if we get a valid object passed to it. Any error messages will be reported on stderr
  187. // but since no normal user should ever see this it should be fine
  188. //
  189. QObject::connect(engine, &QQmlApplicationEngine::objectCreated, [=](QObject* object, const QUrl& url)
  190. {
  191. Q_UNUSED(url);
  192. if (object == nullptr)
  193. throw FatalException(QObject::tr("Failed to parse application engine script."));
  194. KonvergoWindow* window = Globals::MainWindow();
  195. QObject* webChannel = qvariant_cast<QObject*>(window->property("webChannel"));
  196. Q_ASSERT(webChannel);
  197. ComponentManager::Get().setWebChannel(qobject_cast<QWebChannel*>(webChannel));
  198. QObject::connect(uniqueApp, &UniqueApplication::otherApplicationStarted, window, &KonvergoWindow::otherAppFocus);
  199. });
  200. engine->load(QUrl(QStringLiteral("qrc:/ui/webview.qml")));
  201. Log::UpdateLogLevel();
  202. // run our application
  203. int ret = app.exec();
  204. delete uniqueApp;
  205. Globals::EngineDestroy();
  206. Codecs::Uninit();
  207. Log::Uninit();
  208. return ret;
  209. }
  210. catch (FatalException& e)
  211. {
  212. QLOG_FATAL() << "Unhandled FatalException:" << qPrintable(e.message());
  213. QApplication errApp(argc, argv);
  214. auto msg = new ErrorMessage(e.message(), true);
  215. msg->show();
  216. errApp.exec();
  217. Codecs::Uninit();
  218. Log::Uninit();
  219. return 1;
  220. }
  221. }