// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #include "libvideostitch/config.hpp" #include "uniqueqapplication.hpp" #include "vslocalserver.hpp" #include "vslocalsocket.hpp" #include "vssettings.hpp" #include "libvideostitch-base/logmanager.hpp" #include #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER #include #endif class UniqueQApplication::Impl { public: QSharedMemory sharedMem; bool isUnique; QLocalSocket* socket; Packet packetToSend; QString key; #ifdef _MSC_VER HANDLE uniqueVSMutex; // used for the Windows installer - VSA-1525 #endif bool restartApp; }; UniqueQApplication::UniqueQApplication(int& argc, char* argv[], QString applicationKey) : QApplication(argc, argv), impl(new Impl()) { impl->isUnique = false; impl->socket = new QLocalSocket(this); impl->key = applicationKey; impl->restartApp = false; setUpLocalServer(); } UniqueQApplication::~UniqueQApplication() { cleanup(); #ifdef _MSC_VER if (impl->uniqueVSMutex) { ReleaseMutex(impl->uniqueVSMutex); } #endif if (impl->restartApp) { QProcess proc; proc.startDetached(QApplication::applicationFilePath(), QStringList()); } } UniqueQApplication* UniqueQApplication::instance() { return qobject_cast(qApp); } void UniqueQApplication::initializeOrganization() { QCoreApplication::setOrganizationName(VIDEOSTITCH_ORG_NAME); QCoreApplication::setOrganizationDomain(VIDEOSTITCH_ORG_DOMAIN); } void UniqueQApplication::cleanup() { // close the logManager in its own thread QMetaObject::invokeMethod(VideoStitch::Helper::LogManager::getInstance(), "close", Qt::BlockingQueuedConnection); QThread* loggerThread = findChild("logger.thread", Qt::FindDirectChildrenOnly); if (loggerThread) { loggerThread->quit(); loggerThread->wait(100); } QThread* serverThread = findChild("local.server.thread", Qt::FindDirectChildrenOnly); if (serverThread) { serverThread->quit(); serverThread->wait(100); } impl->sharedMem.detach(); } void UniqueQApplication::loadStylesheetFile(QString stylesheetPath, QString stylesheetVariablesPath, QString commonStylesheetVariablesPath) { if (!QFile::exists(stylesheetPath)) { qDebug() << QString("Style %1 not found.").arg(stylesheetPath); return; } QFile stylesheetFile(stylesheetPath); if (!stylesheetFile.open(QFile::ReadOnly)) { qDebug() << QString("Could not open stylesheet %1.").arg(stylesheetPath); return; } QString stylesheetContent = QLatin1String(stylesheetFile.readAll()); auto applyStylesheetVariables = [&stylesheetContent](QString variablesPath) { if (variablesPath.isEmpty()) { return; } if (!QFile::exists(variablesPath)) { qWarning() << QString("Style variables %1 not found.").arg(variablesPath); } QSettings variablesFile(variablesPath, QSettings::IniFormat); QStringList keys = variablesFile.childKeys(); for (QString key : keys) { QString value = variablesFile.value(key).toString(); for (QString otherKey : keys) { QString otherValue = variablesFile.value(otherKey).toString(); otherValue.replace(key, value); variablesFile.setValue(otherKey, otherValue); } } for (QString key : variablesFile.childKeys()) { stylesheetContent.replace(key, variablesFile.value(key).toString()); } }; applyStylesheetVariables(stylesheetVariablesPath); applyStylesheetVariables(commonStylesheetVariablesPath); setStyleSheet(styleSheet() + stylesheetContent); } void UniqueQApplication::loadTranslationFiles() { QString language = VSSettings::getSettings()->getLanguage(); QDir dir(applicationDirPath() + QString("/translations")); QString nameFilter = QString("*_%0.qm").arg(language); QStringList translationFiles = dir.entryList(QStringList() << nameFilter, QDir::Files); if (translationFiles.isEmpty() && language != "en") { nameFilter = QString("*_en.qm"); translationFiles = dir.entryList(QStringList() << nameFilter, QDir::Files); } auto logManager = VideoStitch::Helper::LogManager::getInstance(); logManager->writeToLogFile(QString("Found %0 translation files").arg(translationFiles.count())); for (QString filePath : translationFiles) { QTranslator* translator = new QTranslator(this); bool loaded = translator->load(filePath, dir.absolutePath()); if (loaded) { if (installTranslator(translator)) { logManager->writeToLogFile(QString("Translation file %0 loaded and installed").arg(filePath)); } else { logManager->writeToLogFile(QString("Translation file %0 loaded but failed to install").arg(filePath)); } } else { delete translator; logManager->writeToLogFile(QString("Translation file %0 failed to load").arg(filePath)); } } } QString UniqueQApplication::getYoutubeUrl() const { return property("youtube-url").toString(); } QString UniqueQApplication::getTutorialUrl() const { return property("tutorial-url").toString(); } void UniqueQApplication::setYoutubeUrl(QString url) { setProperty("youtube-url", url); } void UniqueQApplication::setTutorialUrl(QString url) { setProperty("tutorial-url", url); } bool UniqueQApplication::uniqueInstance() const { return impl->isUnique && impl->sharedMem.isAttached(); } void UniqueQApplication::setUpLogger() { QThread* loggerThread = new QThread(this); loggerThread->setObjectName("logger.thread"); loggerThread->start(); VideoStitch::Helper::LogManager* logManager = VideoStitch::Helper::LogManager::getInstance(); logManager->moveToThread(loggerThread); logManager->setUpLogger(); } void UniqueQApplication::incomingClient(VSLocalSocket* instance) { connect(instance, &VSLocalSocket::messageReceived, this, &UniqueQApplication::receiveMessage); } void UniqueQApplication::receiveMessage(const Packet& packet) { emit messageAvailable(packet); } void UniqueQApplication::connectAndSend(const Packet& pack, QString host, bool andDie) { impl->packetToSend = pack; connect(impl->socket, SIGNAL(connected()), this, SLOT(sendPacket())); if (andDie) { connect(impl->socket, SIGNAL(bytesWritten(qint64)), this, SLOT(quit())); } else { connect(impl->socket, SIGNAL(bytesWritten(qint64)), this, SLOT(closeSocket())); } impl->socket->connectToServer(host, QIODevice::WriteOnly); } void UniqueQApplication::sendPacket() { QByteArray packet; QDataStream out(&packet, QIODevice::WriteOnly); out << impl->packetToSend; impl->socket->write(packet); impl->socket->flush(); } bool UniqueQApplication::event(QEvent* event) { switch (event->type()) { case QEvent::FileOpen: emit reqOpenFile(QStringList() << static_cast(event)->file()); return true; default: return QApplication::event(event); } } void UniqueQApplication::setUpLocalServer() { QThread* serverThread = new QThread(this); serverThread->setObjectName("local.server.thread"); QScopedPointer localServer(new VSLocalServer()); connect(serverThread, &QThread::finished, localServer.data(), &VSLocalServer::deleteLater); connect(localServer.data(), &VSLocalServer::newClient, this, &UniqueQApplication::incomingClient, Qt::BlockingQueuedConnection); qRegisterMetaType("Packet"); impl->sharedMem.setKey(impl->key); if (impl->sharedMem.attach()) { #ifndef Q_OS_WIN impl->isUnique = localServer->tryToStartServer(impl->key); if (impl->isUnique) { localServer->moveToThread(serverThread); localServer.take(); serverThread->start(); } #else impl->isUnique = false; #endif } else { impl->isUnique = true; if (!impl->sharedMem.create(1)) { return; } #ifdef _MSC_VER QString stringToConvert("VideoStitch-" + impl->key); impl->uniqueVSMutex = CreateMutex(NULL, true, (const wchar_t*)stringToConvert.utf16()); if ((impl->uniqueVSMutex == NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { assert(0); } #endif localServer->tryToStartServer(impl->key); localServer->moveToThread(serverThread); localServer.take(); serverThread->start(); } } void UniqueQApplication::closeSocket() { impl->socket->disconnectFromServer(); } void UniqueQApplication::restartApplication() { impl->restartApp = true; quit(); }