blob: 938a0eb55c506ddc747d2c4c3ed0218d99705db2 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001/*
2 * Copyright (C) 2015-2020 by Savoir-faire Linux
3 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
4 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
5 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "mainapplication.h"
22
23#include "accountadapter.h"
24#include "accountlistmodel.h"
25#include "accountstomigratelistmodel.h"
26#include "audiocodeclistmodel.h"
27#include "audioinputdevicemodel.h"
28#include "audiomanagerlistmodel.h"
29#include "audiooutputdevicemodel.h"
30#include "avadapter.h"
31#include "bannedlistmodel.h"
32#include "calladapter.h"
33#include "clientwrapper.h"
34#include "contactadapter.h"
35#include "conversationsadapter.h"
36#include "deviceitemlistmodel.h"
37#include "pluginitemlistmodel.h"
38#include "preferenceitemlistmodel.h"
39#include "distantrenderer.h"
40#include "globalinstances.h"
41#include "globalsystemtray.h"
42#include "messagesadapter.h"
43#include "namedirectory.h"
44#include "pixbufmanipulator.h"
45#include "previewrenderer.h"
46#include "qrimageprovider.h"
47#include "settingsadaptor.h"
48#include "tintedbuttonimageprovider.h"
49#include "utils.h"
50#include "version.h"
51#include "videocodeclistmodel.h"
52#include "videoformatfpsmodel.h"
53#include "videoformatresolutionmodel.h"
54#include "videoinputdevicemodel.h"
55
56#include <QFontDatabase>
57#include <QQmlContext>
58#include <QtWebEngine>
59
60#include <locale.h>
61
62#ifdef Q_OS_WIN
63#include <windows.h>
64#endif
65
66#if defined _MSC_VER && !COMPILE_ONLY
67#include <gnutls/gnutls.h>
68#endif
69
70MainApplication::MainApplication(int &argc, char **argv)
71 : QApplication(argc, argv)
72 , engine_(new QQmlApplicationEngine())
73{
74 QObject::connect(this, &QApplication::aboutToQuit, [this] { exitApp(); });
75}
76
77void
78MainApplication::applicationInitialization()
79{
80 /*
81 * Some attributes are needed to be set before the creation of the application.
82 */
83 QApplication::setApplicationName("Ring");
84 QApplication::setOrganizationDomain("jami.net");
85 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
86 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
87 QApplication::setQuitOnLastWindowClosed(false);
88 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
89#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
90 QApplication::setHighDpiScaleFactorRoundingPolicy(
91 Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
92 /*
93 * Initialize QtWebEngine.
94 */
95 QtWebEngine::initialize();
96#endif
97}
98
99void
100MainApplication::consoleDebug()
101{
102#ifdef Q_OS_WIN
103 AllocConsole();
104 SetConsoleCP(CP_UTF8);
105
106 freopen("CONOUT$", "w", stdout);
107 freopen("CONOUT$", "w", stderr);
108
109 COORD coordInfo;
110 coordInfo.X = 130;
111 coordInfo.Y = 9000;
112
113 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coordInfo);
114 SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
115#endif
116}
117
118void
119MainApplication::vsConsoleDebug()
120{
121#ifdef _MSC_VER
122 /*
123 * Print debug to output window if using VS.
124 */
125 QObject::connect(&LRCInstance::behaviorController(),
126 &lrc::api::BehaviorController::debugMessageReceived,
127 [](const QString &message) {
128 OutputDebugStringA((message + "\n").toStdString().c_str());
129 });
130#endif
131}
132
133void
134MainApplication::fileDebug(QFile *debugFile)
135{
136 QObject::connect(&LRCInstance::behaviorController(),
137 &lrc::api::BehaviorController::debugMessageReceived,
138 [debugFile](const QString &message) {
139 if (debugFile->open(QIODevice::WriteOnly | QIODevice::Append)) {
140 auto msg = (message + "\n").toStdString().c_str();
141 debugFile->write(msg, qstrlen(msg));
142 debugFile->close();
143 }
144 });
145}
146
147void
148MainApplication::exitApp()
149{
150 GlobalSystemTray::instance().hide();
151#ifdef Q_OS_WIN
152 FreeConsole();
153#endif
154}
155
156char **
157MainApplication::parseInputArgument(int &argc, char *argv[], char *argToParse)
158{
159 /*
160 * Forcefully append argToParse.
161 */
162 int oldArgc = argc;
163 argc = argc + 1 + 1;
164 char **newArgv = new char *[argc];
165 for (int i = 0; i < oldArgc; i++) {
166 newArgv[i] = argv[i];
167 }
168 newArgv[oldArgc] = argToParse;
169 newArgv[oldArgc + 1] = nullptr;
170 return newArgv;
171}
172
173QString
174MainApplication::getDebugFilePath()
175{
176 QDir logPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
177 /*
178 * Since logPath will be .../Ring, we use cdUp to remove it.
179 */
180 logPath.cdUp();
181 return QString(logPath.absolutePath() + "/jami/jami.log");
182}
183
184void
185MainApplication::loadTranslations()
186{
187 auto appDir = qApp->applicationDirPath() + "/";
188 const auto locale_name = QLocale::system().name();
189 const auto locale_lang = locale_name.split('_')[0];
190
191 QTranslator *qtTranslator_lang = new QTranslator(this);
192 QTranslator *qtTranslator_name = new QTranslator(this);
193 if (locale_name != locale_lang) {
194 if (qtTranslator_lang->load("qt_" + locale_lang,
195 QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
196 installTranslator(qtTranslator_lang);
197 }
198 qtTranslator_name->load("qt_" + locale_name,
199 QLibraryInfo::location(QLibraryInfo::TranslationsPath));
200 installTranslator(qtTranslator_name);
201
202 QTranslator *lrcTranslator_lang = new QTranslator(this);
203 QTranslator *lrcTranslator_name = new QTranslator(this);
204 if (locale_name != locale_lang) {
205 if (lrcTranslator_lang->load(appDir + "share/libringclient/translations/lrc_" + locale_lang))
206 installTranslator(lrcTranslator_lang);
207 }
208 if (lrcTranslator_name->load(appDir + "share/libringclient/translations/lrc_" + locale_name))
209 installTranslator(lrcTranslator_name);
210
211 QTranslator *mainTranslator_lang = new QTranslator(this);
212 QTranslator *mainTranslator_name = new QTranslator(this);
213 if (locale_name != locale_lang) {
214 if (mainTranslator_lang->load(appDir + "share/ring/translations/ring_client_windows_"
215 + locale_lang))
216 installTranslator(mainTranslator_lang);
217 }
218 if (mainTranslator_name->load(appDir + "share/ring/translations/ring_client_windows_"
219 + locale_name))
220 installTranslator(mainTranslator_name);
221}
222
223void
224MainApplication::initLrc()
225{
226 /*
227 * Init mainwindow and finish splash when mainwindow shows up.
228 */
229 std::atomic_bool isMigrating(false);
230 LRCInstance::init(
231 [this, &isMigrating] {
232 /*
233 * TODO: splash screen for account migration.
234 */
235 isMigrating = true;
236 while (isMigrating) {
237 this->processEvents();
238 }
239 },
240 [this, &isMigrating] {
241 while (!isMigrating) {
242 std::this_thread::sleep_for(std::chrono::milliseconds(10));
243 }
244 isMigrating = false;
245 });
246 LRCInstance::subscribeToDebugReceived();
247 LRCInstance::getAPI().holdConferences = false;
248}
249
250void
251MainApplication::processInputArgument(bool &startMinimized)
252{
253 debugFile_ = std::make_unique<QFile>(getDebugFilePath());
254 QString uri = "";
255
256 for (auto string : QCoreApplication::arguments()) {
257 if (string.startsWith("jami:")) {
258 uri = string;
259 } else {
260 if (string == "-m" || string == "--minimized") {
261 startMinimized = true;
262 }
263 auto dbgFile = string == "-f" || string == "--file";
264 auto dbgConsole = string == "-c" || string == "--vsconsole";
265 if (dbgFile || dbgConsole) {
266 if (dbgFile) {
267 debugFile_->open(QIODevice::WriteOnly | QIODevice::Truncate);
268 debugFile_->close();
269 fileDebug(debugFile_.get());
270 }
271#ifdef _MSC_VER
272 if (dbgConsole) {
273 vsConsoleDebug();
274 }
275#endif
276 }
277 }
278 }
279}
280
281void
282MainApplication::setApplicationFont()
283{
284 QFont font;
285 font.setFamily("Segoe UI");
286 setFont(font);
287 QFontDatabase::addApplicationFont(":/images/FontAwesome.otf");
288}
289
290void
291MainApplication::qmlInitialization()
292{
293 /*
294 * Register accountListModel type.
295 */
296 QML_REGISTERTYPE(AccountListModel, 1, 0);
297 QML_REGISTERTYPE(DeviceItemListModel, 1, 0);
298 QML_REGISTERTYPE(PluginItemListModel, 1, 0);
299 QML_REGISTERTYPE(PreferenceItemListModel, 1, 0);
300 QML_REGISTERTYPE(BannedListModel, 1, 0);
301 QML_REGISTERTYPE(VideoCodecListModel, 1, 0);
302 QML_REGISTERTYPE(AudioCodecListModel, 1, 0);
303 QML_REGISTERTYPE(AccountsToMigrateListModel, 1, 0);
304 QML_REGISTERTYPE(AudioInputDeviceModel, 1, 0);
305 QML_REGISTERTYPE(AudioOutputDeviceModel, 1, 0);
306 QML_REGISTERTYPE(AudioManagerListModel, 1, 0);
307 QML_REGISTERTYPE(VideoInputDeviceModel, 1, 0);
308 QML_REGISTERTYPE(VideoFormatResolutionModel, 1, 0);
309 QML_REGISTERTYPE(VideoFormatFpsModel, 1, 0);
310 /*
311 * Register QQuickItem type.
312 */
313 QML_REGISTERTYPE(PreviewRenderer, 1, 0);
314 QML_REGISTERTYPE(VideoCallPreviewRenderer, 1, 0);
315 QML_REGISTERTYPE(DistantRenderer, 1, 0);
316 QML_REGISTERTYPE(PhotoboothPreviewRender, 1, 0)
317
318 /*
319 * Adapter - qmlRegisterSingletonType.
320 * Note: in future, if lrc is fully compatible with qml (C++ struct
321 * is readable in qml), the adapters can be optimized away.
322 */
323 QML_REGISTERSINGLETONTYPE_URL(QStringLiteral("qrc:/src/constant/JamiTheme.qml"),
324 JamiTheme,
325 1,
326 0);
327 QML_REGISTERSINGLETONTYPE(CallAdapter, 1, 0);
328
329 QML_REGISTERSINGLETONTYPE(MessagesAdapter, 1, 0);
330 QML_REGISTERSINGLETONTYPE(ConversationsAdapter, 1, 0);
331 QML_REGISTERSINGLETONTYPE(AvAdapter, 1, 0);
332 QML_REGISTERSINGLETONTYPE(ContactAdapter, 1, 0);
333 QML_REGISTERSINGLETONTYPE(ClientWrapper, 1, 0);
334
335 //QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(AccountAdapter, 1, 0);
336 //QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(UtilsAdapter, 1, 0);
337 QML_REGISTERUNCREATABLE(AccountAdapter, 1, 0);
338 QML_REGISTERUNCREATABLE(UtilsAdapter, 1, 0);
339 QML_REGISTERUNCREATABLE(SettingsAdaptor, 1, 0);
340 QML_REGISTERUNCREATABLE(NameDirectory, 1, 0);
341 QML_REGISTERUNCREATABLE(LRCInstance, 1, 0);
342
343 /*
344 * Lrc models - qmlRegisterUncreatableType & Q_DECLARE_METATYPE.
345 * This to make lrc models recognizable in qml.
346 */
347 QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewAccountModel, lrc::api, 1, 0);
348 QML_REGISTERUNCREATABLE_IN_NAMESPACE(BehaviorController, lrc::api, 1, 0);
349 QML_REGISTERUNCREATABLE_IN_NAMESPACE(DataTransferModel, lrc::api, 1, 0);
350 QML_REGISTERUNCREATABLE_IN_NAMESPACE(AVModel, lrc::api, 1, 0);
351 QML_REGISTERUNCREATABLE_IN_NAMESPACE(ContactModel, lrc::api, 1, 0);
352 QML_REGISTERUNCREATABLE_IN_NAMESPACE(ConversationModel, lrc::api, 1, 0);
353 QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCallModel, lrc::api, 1, 0);
354 QML_REGISTERUNCREATABLE_IN_NAMESPACE(PluginModel, lrc::api, 1, 0);
355 QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewDeviceModel, lrc::api, 1, 0);
356 QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCodecModel, lrc::api, 1, 0);
357 QML_REGISTERUNCREATABLE_IN_NAMESPACE(PeerDiscoveryModel, lrc::api, 1, 0);
358
359 /*
360 * Client models - qmlRegisterUncreatableType & Q_DECLARE_METATYPE.
361 * This to make client models recognizable in qml.
362 */
363 QML_REGISTERUNCREATABLE(RenderManager, 1, 0);
364
365 /*
366 * Namespaces - qmlRegisterUncreatableMetaObject.
367 */
368 QML_REGISTERNAMESPACE(lrc::api::staticMetaObject, "Lrc", 1, 0);
369 QML_REGISTERNAMESPACE(lrc::api::account::staticMetaObject, "Account", 1, 0);
370 QML_REGISTERNAMESPACE(lrc::api::call::staticMetaObject, "Call", 1, 0);
371 QML_REGISTERNAMESPACE(lrc::api::datatransfer::staticMetaObject, "Datatransfer", 1, 0);
372 QML_REGISTERNAMESPACE(lrc::api::interaction::staticMetaObject, "Interaction", 1, 0);
373 QML_REGISTERNAMESPACE(lrc::api::video::staticMetaObject, "Video", 1, 0);
374 QML_REGISTERNAMESPACE(lrc::api::profile::staticMetaObject, "Profile", 1, 0);
375
376 /*
377 * Add image provider.
378 */
379 engine_->addImageProvider(QLatin1String("qrImage"), new QrImageProvider());
380 engine_->addImageProvider(QLatin1String("tintedPixmap"), new TintedButtonImageProvider());
381
382 engine_->load(QUrl(QStringLiteral("qrc:/src/MainApplicationWindow.qml")));
383}
384
385MainApplication::~MainApplication() {}
386
387bool
388MainApplication::applicationSetup()
389{
390#ifdef Q_OS_LINUX
391 if (!getenv("QT_QPA_PLATFORMTHEME"))
392 setenv("QT_QPA_PLATFORMTHEME", "gtk3", true);
393#endif
394
395 /*
396 * Start debug console.
397 */
398 for (auto string : QCoreApplication::arguments()) {
399 if (string == "-d" || string == "--debug") {
400 consoleDebug();
401 }
402 }
403
404 /*
405 * Remove old version files.
406 */
407 Utils::removeOldVersions();
408
409 /*
410 * Load translations.
411 */
412 loadTranslations();
413
414 /*
415 * Set font.
416 */
417 setApplicationFont();
418
419#if defined _MSC_VER && !COMPILE_ONLY
420 gnutls_global_init();
421#endif
422
423 /*
424 * Init pixmap manipulator.
425 */
426 GlobalInstances::setPixmapManipulator(std::make_unique<PixbufManipulator>());
427
428 /*
429 * Init lrc and its possible migration ui.
430 */
431 initLrc();
432
433 /*
434 * Process input argument.
435 */
436 bool startMinimized{false};
437 processInputArgument(startMinimized);
438
439 /*
440 * Create jami.net settings in Registry if it is not presented.
441 */
442 QSettings settings("jami.net", "Jami");
443
444 /*
445 * Initialize qml components.
446 */
447 qmlInitialization();
448
449 return true;
450}