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