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