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