main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. #include <locale.h>
  2. #include <QGuiApplication>
  3. #include <QFileInfo>
  4. #include <QIcon>
  5. #include <QtQml>
  6. #include <QtWebEngine/qtwebengineglobal.h>
  7. #include <shared/Names.h>
  8. #include "system/SystemComponent.h"
  9. #include "system/UpdateManager.h"
  10. #include "QsLog.h"
  11. #include "Paths.h"
  12. #include "player/PlayerComponent.h"
  13. #include "breakpad/CrashDumps.h"
  14. #include "Version.h"
  15. #include "settings/SettingsComponent.h"
  16. #include "settings/SettingsSection.h"
  17. #include "ui/KonvergoWindow.h"
  18. #include "ui/KonvergoEngine.h"
  19. #include "UniqueApplication.h"
  20. #include "utils/HelperLauncher.h"
  21. #if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
  22. #include "SignalManager.h"
  23. #endif
  24. using namespace QsLogging;
  25. /////////////////////////////////////////////////////////////////////////////////////////
  26. static void preinitQt()
  27. {
  28. QCoreApplication::setApplicationName(Names::MainName());
  29. QCoreApplication::setApplicationVersion(Version::GetVersionString());
  30. QCoreApplication::setOrganizationDomain("plex.tv");
  31. #ifdef Q_OS_WIN32
  32. QVariant useOpengl = SettingsComponent::readPreinitValue(SETTINGS_SECTION_MAIN, "useOpenGL");
  33. // Warning: this must be the same as the default value as declared in
  34. // the settings_description.json file, or confusion will result.
  35. if (useOpengl.type() != QMetaType::Bool)
  36. useOpengl = true;
  37. if (useOpengl.toBool())
  38. QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
  39. else
  40. QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
  41. #endif
  42. }
  43. /////////////////////////////////////////////////////////////////////////////////////////
  44. static void qtMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
  45. {
  46. QByteArray localMsg = msg.toLocal8Bit();
  47. QString prefix;
  48. if (context.line)
  49. prefix = QString("%1:%2:%3: ").arg(context.file).arg(context.line).arg(context.function);
  50. QString text = prefix + msg;
  51. switch (type)
  52. {
  53. case QtDebugMsg:
  54. QLOG_DEBUG() << text;
  55. break;
  56. case QtInfoMsg:
  57. QLOG_INFO() << text;
  58. break;
  59. case QtWarningMsg:
  60. QLOG_WARN() << text;
  61. break;
  62. case QtCriticalMsg:
  63. QLOG_ERROR() << text;
  64. break;
  65. case QtFatalMsg:
  66. QLOG_FATAL() << text;
  67. break;
  68. }
  69. }
  70. /////////////////////////////////////////////////////////////////////////////////////////
  71. static void elidePattern(QString& msg, const QString& substring, int chars)
  72. {
  73. int start = 0;
  74. while (true)
  75. {
  76. start = msg.indexOf(substring, start);
  77. if (start < 0 || start + substring.length() + chars > msg.length())
  78. break;
  79. start += substring.length();
  80. for (int n = 0; n < chars; n++)
  81. msg[start + n] = QChar('x');
  82. }
  83. }
  84. /////////////////////////////////////////////////////////////////////////////////////////
  85. static void processLog(QString& msg)
  86. {
  87. elidePattern(msg, "X-Plex-Token=", 20);
  88. elidePattern(msg, "X-Plex-Token%3D", 20);
  89. }
  90. /////////////////////////////////////////////////////////////////////////////////////////
  91. void initLogger()
  92. {
  93. // Note where the logfile is going to be
  94. qDebug("Logging to %s", qPrintable(Paths::logDir(Names::MainName() + ".log")));
  95. // init logging.
  96. DestinationPtr dest = DestinationFactory::MakeFileDestination(
  97. Paths::logDir(Names::MainName() + ".log"),
  98. EnableLogRotationOnOpen,
  99. MaxSizeBytes(1024 * 1024),
  100. MaxOldLogCount(9));
  101. Logger::instance().addDestination(dest);
  102. Logger::instance().setLoggingLevel(DebugLevel);
  103. Logger::instance().setProcessingCallback(processLog);
  104. qInstallMessageHandler(qtMessageOutput);
  105. }
  106. static QsLogging::Level logLevelFromString(const QString& str)
  107. {
  108. if (str == "trace") return QsLogging::Level::TraceLevel;
  109. if (str == "debug") return QsLogging::Level::DebugLevel;
  110. if (str == "info") return QsLogging::Level::InfoLevel;
  111. if (str == "warn") return QsLogging::Level::WarnLevel;
  112. if (str == "error") return QsLogging::Level::ErrorLevel;
  113. if (str == "fatal") return QsLogging::Level::FatalLevel;
  114. if (str == "disable") return QsLogging::Level::OffLevel;
  115. // if not valid, use default
  116. return QsLogging::Level::DebugLevel;
  117. }
  118. static void updateLogLevel()
  119. {
  120. QString level = SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "logLevel").toString();
  121. if (level.size())
  122. {
  123. QLOG_INFO() << "Setting log level to:" << level;
  124. Logger::instance().setLoggingLevel(logLevelFromString(level));
  125. }
  126. }
  127. /////////////////////////////////////////////////////////////////////////////////////////
  128. char** appendCommandLineArguments(int *argc, char **argv)
  129. {
  130. static char *newArgs[16];
  131. QList<QString> argList;
  132. // Copy argv list to our StringList
  133. for (int i=0; i < *argc; i++)
  134. {
  135. argList << QString(argv[i]);
  136. }
  137. // add any required additionnal commandline argument
  138. #if KONVERGO_OPENELEC
  139. // on RPI with webengine, OpenGL contexts are shared statically with webengine
  140. // which avoids proper reset when switching display mode
  141. // On OE we also need that because there is a crash with OZONE otherwise
  142. argList << "--disable-gpu";
  143. #endif
  144. // with webengine we need those to have a proper scaling of the webview in the window
  145. argList << "--enable-viewport";
  146. argList << "--enable-viewport-meta";
  147. // Now rebuild our argc, argv list
  148. *argc = argList.size();
  149. for(int iarg=0; iarg < argList.size(); iarg++)
  150. {
  151. newArgs[iarg] = (char*)malloc(256);
  152. strcpy(newArgs[iarg], argList.value(iarg).toStdString().c_str());
  153. }
  154. return (char**)newArgs;
  155. }
  156. /////////////////////////////////////////////////////////////////////////////////////////
  157. int main(int argc, char *argv[])
  158. {
  159. try
  160. {
  161. for (int n = 1; n < argc; n++) {
  162. if (strcmp(argv[n], "--licenses") == 0) {
  163. QFile licenses(":/misc/licenses.txt");
  164. licenses.open(QIODevice::ReadOnly | QIODevice::Text);
  165. QByteArray contents = licenses.readAll();
  166. printf("%.*s\n", (int)contents.size(), contents.data());
  167. return 0;
  168. }
  169. }
  170. int newArgc = argc;
  171. char **newArgv = appendCommandLineArguments(&newArgc, argv);
  172. // Supress SSL related warnings on OSX
  173. // See https://bugreports.qt.io/browse/QTBUG-43173 for more info
  174. //
  175. #ifdef Q_OS_MAC
  176. qputenv("QT_LOGGING_RULES", "qt.network.ssl.warning=false");
  177. // Request OpenGL 4.1 if possible on OSX, otherwise it defaults to 2.0
  178. // This needs to be done before we create the QGuiApplication
  179. //
  180. QSurfaceFormat format = QSurfaceFormat::defaultFormat();
  181. format.setMajorVersion(3);
  182. format.setMinorVersion(2);
  183. format.setProfile(QSurfaceFormat::CoreProfile);
  184. QSurfaceFormat::setDefaultFormat(format);
  185. #endif
  186. preinitQt();
  187. QGuiApplication app(newArgc, newArgv);
  188. app.setWindowIcon(QIcon(":/images/icon.png"));
  189. // init breakpad.
  190. setupCrashDumper();
  191. UniqueApplication* uniqueApp = new UniqueApplication();
  192. if (!uniqueApp->ensureUnique())
  193. return EXIT_SUCCESS;
  194. #ifdef Q_OS_UNIX
  195. // install signals handlers for proper app closing.
  196. SignalManager signalManager(&app);
  197. Q_UNUSED(signalManager);
  198. #endif
  199. initLogger();
  200. QLOG_INFO() << "Starting Plex Media Player version:" << qPrintable(Version::GetVersionString()) << "build date:" << qPrintable(Version::GetBuildDate());
  201. QLOG_INFO() << qPrintable(QString(" Running on: %1 [%2] arch %3").arg(QSysInfo::prettyProductName()).arg(QSysInfo::kernelVersion()).arg(QSysInfo::currentCpuArchitecture()));
  202. QLOG_INFO() << " Qt Version:" << QT_VERSION_STR << qPrintable(QString("[%1]").arg(QSysInfo::buildAbi()));
  203. // Quit app and apply update if we find one.
  204. if (UpdateManager::CheckForUpdates())
  205. {
  206. app.quit();
  207. return 0;
  208. }
  209. #ifdef Q_OS_WIN32
  210. initD3DDevice();
  211. #endif
  212. #ifdef Q_OS_UNIX
  213. setlocale(LC_NUMERIC, "C");
  214. #endif
  215. // Initialize all the components. This needs to be done
  216. // early since most everything else relies on it
  217. //
  218. ComponentManager::Get().initialize();
  219. // enable remote inspection if we have the correct setting for it.
  220. if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "remoteInspector").toBool())
  221. qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "0.0.0.0:9992");
  222. QtWebEngine::initialize();
  223. // Qt and QWebEngineProfile set the locale, which breaks parsing and
  224. // formatting float numbers in a few countries.
  225. #ifdef Q_OS_UNIX
  226. setlocale(LC_NUMERIC, "C");
  227. #endif
  228. // start our helper
  229. HelperLauncher::Get().connectToHelper();
  230. // load QtWebChannel so that we can register our components with it.
  231. QQmlApplicationEngine *engine = KonvergoEngine::Get();
  232. KonvergoWindow::RegisterClass();
  233. engine->rootContext()->setContextProperty("components", &ComponentManager::Get().getQmlPropertyMap());
  234. // This controls how big the web view will zoom using semantic zoom
  235. // over a specific number of pixels and we run out of space for on screen
  236. // tiles in chromium. This only happens on OSX since on other platforms
  237. // we can use the GPU to transfer tiles directly but we set the limit on all platforms
  238. // to keep it consistent.
  239. //
  240. // See more discussion in: https://github.com/plexinc/plex-media-player/issues/10
  241. // The number of pixels here are REAL pixels, the code in webview.qml will compensate
  242. // for a higher DevicePixelRatio
  243. //
  244. engine->rootContext()->setContextProperty("webMaxHeight", 1440);
  245. // the only way to detect if QML parsing fails is to hook to this signal and then see
  246. // if we get a valid object passed to it. Any error messages will be reported on stderr
  247. // but since no normal user should ever see this it should be fine
  248. //
  249. QObject::connect(engine, &QQmlApplicationEngine::objectCreated, [=](QObject* object, const QUrl& url)
  250. {
  251. Q_UNUSED(url);
  252. if (object == 0)
  253. throw FatalException(QObject::tr("Failed to parse application engine script."));
  254. QObject* rootObject = engine->rootObjects().first();
  255. QObject* webChannel = qvariant_cast<QObject*>(rootObject->property("webChannel"));
  256. Q_ASSERT(webChannel);
  257. ComponentManager::Get().setWebChannel(qobject_cast<QWebChannel*>(webChannel));
  258. KonvergoWindow* window = qobject_cast<KonvergoWindow*>(rootObject);
  259. Q_ASSERT(window);
  260. QObject::connect(uniqueApp, &UniqueApplication::otherApplicationStarted, window, &KonvergoWindow::otherAppFocus);
  261. });
  262. engine->load(QUrl(QStringLiteral("qrc:/ui/webview.qml")));
  263. updateLogLevel();
  264. // run our application
  265. int ret = app.exec();
  266. delete KonvergoEngine::Get();
  267. delete uniqueApp;
  268. return ret;
  269. }
  270. catch (FatalException& e)
  271. {
  272. QLOG_FATAL() << "Unhandled FatalException:" << qPrintable(e.message());
  273. QGuiApplication app(argc, argv);
  274. QString text = e.message() + "<br>" + QObject::tr("Please visit Plex support forums for support.");
  275. QQmlApplicationEngine* engine = new QQmlApplicationEngine(NULL);
  276. engine->rootContext()->setContextProperty("errorTitle", QObject::tr("A critical error occurred."));
  277. engine->rootContext()->setContextProperty("errorText", text);
  278. engine->load(QUrl(QStringLiteral("qrc:/ui/errormessage.qml")));
  279. app.exec();
  280. return 1;
  281. }
  282. }