qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
nexus.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2014-2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 
21 #include "nexus.h"
22 #include "persistence/settings.h"
23 #include "src/core/core.h"
24 #include "src/core/coreav.h"
25 #include "src/model/groupinvite.h"
26 #include "src/model/status.h"
28 #include "src/widget/widget.h"
29 #include "video/camerasource.h"
30 #include "widget/gui.h"
31 #include "widget/loginscreen.h"
32 #include <QApplication>
33 #include <QCommandLineParser>
34 #include <QDebug>
35 #include <QDesktopWidget>
36 #include <QThread>
37 #include <cassert>
38 #include "audio/audio.h"
39 #include <vpx/vpx_image.h>
40 
41 #ifdef Q_OS_MAC
42 #include <QActionGroup>
43 #include <QMenuBar>
44 #include <QSignalMapper>
45 #include <QWindow>
46 #endif
47 
56 Q_DECLARE_OPAQUE_POINTER(ToxAV*)
57 
58 static Nexus* nexus{nullptr};
59 
60 Nexus::Nexus(QObject* parent)
61  : QObject(parent)
62  , profile{nullptr}
63  , widget{nullptr}
64 {}
65 
67 {
68  delete widget;
69  widget = nullptr;
70  delete profile;
71  profile = nullptr;
72  emit saveGlobal();
73 #ifdef Q_OS_MAC
74  delete globalMenuBar;
75 #endif
76 }
77 
85 {
86  qDebug() << "Starting up";
87 
88  // Setup the environment
89  qRegisterMetaType<Status::Status>("Status::Status");
90  qRegisterMetaType<vpx_image>("vpx_image");
91  qRegisterMetaType<uint8_t>("uint8_t");
92  qRegisterMetaType<uint16_t>("uint16_t");
93  qRegisterMetaType<uint32_t>("uint32_t");
94  qRegisterMetaType<const int16_t*>("const int16_t*");
95  qRegisterMetaType<int32_t>("int32_t");
96  qRegisterMetaType<int64_t>("int64_t");
97  qRegisterMetaType<size_t>("size_t");
98  qRegisterMetaType<QPixmap>("QPixmap");
99  qRegisterMetaType<Profile*>("Profile*");
100  qRegisterMetaType<ToxAV*>("ToxAV*");
101  qRegisterMetaType<ToxFile>("ToxFile");
102  qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
103  qRegisterMetaType<std::shared_ptr<VideoFrame>>("std::shared_ptr<VideoFrame>");
104  qRegisterMetaType<ToxPk>("ToxPk");
105  qRegisterMetaType<ToxId>("ToxId");
106  qRegisterMetaType<ToxPk>("GroupId");
107  qRegisterMetaType<ToxPk>("ContactId");
108  qRegisterMetaType<GroupInvite>("GroupInvite");
109  qRegisterMetaType<ReceiptNum>("ReceiptNum");
110  qRegisterMetaType<RowId>("RowId");
111  qRegisterMetaType<uint64_t>("uint64_t");
112  qRegisterMetaType<ExtensionSet>("ExtensionSet");
113 
114  qApp->setQuitOnLastWindowClosed(false);
115 
116 #ifdef Q_OS_MAC
117  // TODO: still needed?
118  globalMenuBar = new QMenuBar();
119  dockMenu = new QMenu(globalMenuBar);
120 
121  viewMenu = globalMenuBar->addMenu(QString());
122 
123  windowMenu = globalMenuBar->addMenu(QString());
124  globalMenuBar->addAction(windowMenu->menuAction());
125 
126  fullscreenAction = viewMenu->addAction(QString());
127  fullscreenAction->setShortcut(QKeySequence::FullScreen);
128  connect(fullscreenAction, &QAction::triggered, this, &Nexus::toggleFullscreen);
129 
130  minimizeAction = windowMenu->addAction(QString());
131  minimizeAction->setShortcut(Qt::CTRL + Qt::Key_M);
132  connect(minimizeAction, &QAction::triggered, [this]() {
133  minimizeAction->setEnabled(false);
134  QApplication::focusWindow()->showMinimized();
135  });
136 
137  windowMenu->addSeparator();
138  frontAction = windowMenu->addAction(QString());
139  connect(frontAction, &QAction::triggered, this, &Nexus::bringAllToFront);
140 
141  QAction* quitAction = new QAction(globalMenuBar);
142  quitAction->setMenuRole(QAction::QuitRole);
143  connect(quitAction, &QAction::triggered, qApp, &QApplication::quit);
144 
145  retranslateUi();
146 #endif
147  showMainGUI();
148 }
149 
153 int Nexus::showLogin(const QString& profileName)
154 {
155  delete widget;
156  widget = nullptr;
157 
158  delete profile;
159  profile = nullptr;
160 
161  LoginScreen loginScreen{profileName};
162  connectLoginScreen(loginScreen);
163 
164  QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
165 
166  // TODO(kriby): Move core out of profile
167  // This is awkward because the core is in the profile
168  // The connection order ensures profile will be ready for bootstrap for now
170  int returnval = loginScreen.exec();
171  if (returnval == QDialog::Rejected) {
172  // Kriby: This will terminate the main application loop, necessary until we refactor
173  // away the split startup/return to login behavior.
174  qApp->quit();
175  }
177  return returnval;
178 }
179 
181 {
182  // kriby: This is a hack until a proper controller is written
183 
184  profile = p;
185 
186  if (profile) {
187  audioControl = std::unique_ptr<IAudioControl>(Audio::makeAudio(*settings));
188  assert(audioControl != nullptr);
190  start();
191  }
192 }
193 
195 {
196  if (this->settings) {
197  QObject::disconnect(this, &Nexus::saveGlobal, this->settings, &Settings::saveGlobal);
198  }
199  this->settings = settings;
200  if (this->settings) {
201  QObject::connect(this, &Nexus::saveGlobal, this->settings, &Settings::saveGlobal);
202  }
203 }
204 
205 void Nexus::connectLoginScreen(const LoginScreen& loginScreen)
206 {
207  // TODO(kriby): Move connect sequences to a controller class object instead
208 
209  // Nexus -> LoginScreen
210  QObject::connect(this, &Nexus::profileLoaded, &loginScreen, &LoginScreen::onProfileLoaded);
211  QObject::connect(this, &Nexus::profileLoadFailed, &loginScreen, &LoginScreen::onProfileLoadFailed);
212  // LoginScreen -> Nexus
213  QObject::connect(&loginScreen, &LoginScreen::createNewProfile, this, &Nexus::onCreateNewProfile);
214  QObject::connect(&loginScreen, &LoginScreen::loadProfile, this, &Nexus::onLoadProfile);
215  // LoginScreen -> Settings
216  QObject::connect(&loginScreen, &LoginScreen::autoLoginChanged, settings, &Settings::setAutoLogin);
217  QObject::connect(&loginScreen, &LoginScreen::autoLoginChanged, settings, &Settings::saveGlobal);
218  // Settings -> LoginScreen
219  QObject::connect(settings, &Settings::autoLoginChanged, &loginScreen,
221 }
222 
224 {
225  // TODO(kriby): Rewrite as view-model connect sequence only, add to a controller class object
226  assert(profile);
227 
228  // Create GUI
229  widget = new Widget(*profile, *audioControl);
230 
231  // Start GUI
232  widget->init();
234 
235  // Zetok protection
236  // There are small instants on startup during which no
237  // profile is loaded but the GUI could still receive events,
238  // e.g. between two modal windows. Disable the GUI to prevent that.
239  GUI::setEnabled(false);
240 
241  // Connections
243 
245 
247  Qt::BlockingQueuedConnection);
248 
249  connect(profile, &Profile::badProxy, widget, &Widget::onBadProxyCore, Qt::BlockingQueuedConnection);
250 
251  profile->startCore();
252 
253  GUI::setEnabled(true);
254 }
255 
260 {
261  if (!nexus)
262  nexus = new Nexus;
263 
264  return *nexus;
265 }
266 
268 {
269  delete nexus;
270  nexus = nullptr;
271 }
272 
278 {
279  Nexus& nexus = getInstance();
280  if (!nexus.profile)
281  return nullptr;
282 
283  return &nexus.profile->getCore();
284 }
285 
291 {
292  return getInstance().profile;
293 }
294 
300 void Nexus::onCreateNewProfile(const QString& name, const QString& pass)
301 {
303  parser = nullptr; // only apply cmdline proxy settings once
304 }
305 
309 void Nexus::onLoadProfile(const QString& name, const QString& pass)
310 {
312  parser = nullptr; // only apply cmdline proxy settings once
313 }
319 {
320  if (!p) {
321  emit profileLoadFailed();
322  // Warnings are issued during respective createNew/load calls
323  return;
324  } else {
325  emit profileLoaded();
326  }
327 
328  emit currentProfileChanged(p);
329 }
330 
331 void Nexus::setParser(QCommandLineParser* parser)
332 {
333  this->parser = parser;
334 }
335 
341 {
342  return getInstance().widget;
343 }
344 
345 #ifdef Q_OS_MAC
346 void Nexus::retranslateUi()
347 {
348  viewMenu->menuAction()->setText(tr("View", "OS X Menu bar"));
349  windowMenu->menuAction()->setText(tr("Window", "OS X Menu bar"));
350  minimizeAction->setText(tr("Minimize", "OS X Menu bar"));
351  frontAction->setText((tr("Bring All to Front", "OS X Menu bar")));
352 }
353 
354 void Nexus::onWindowStateChanged(Qt::WindowStates state)
355 {
356  minimizeAction->setEnabled(QApplication::activeWindow() != nullptr);
357 
358  if (QApplication::activeWindow() != nullptr && sender() == QApplication::activeWindow()) {
359  if (state & Qt::WindowFullScreen)
360  minimizeAction->setEnabled(false);
361 
362  if (state & Qt::WindowFullScreen)
363  fullscreenAction->setText(tr("Exit Fullscreen"));
364  else
365  fullscreenAction->setText(tr("Enter Fullscreen"));
366 
367  updateWindows();
368  }
369 
370  updateWindowsStates();
371 }
372 
373 void Nexus::updateWindows()
374 {
375  updateWindowsArg(nullptr);
376 }
377 
378 void Nexus::updateWindowsArg(QWindow* closedWindow)
379 {
380  QWindowList windowList = QApplication::topLevelWindows();
381  delete windowActions;
382  windowActions = new QActionGroup(this);
383 
384  windowMenu->addSeparator();
385 
386  QAction* dockLast;
387  if (!dockMenu->actions().isEmpty())
388  dockLast = dockMenu->actions().first();
389  else
390  dockLast = nullptr;
391 
392  QWindow* activeWindow;
393 
394  if (QApplication::activeWindow())
395  activeWindow = QApplication::activeWindow()->windowHandle();
396  else
397  activeWindow = nullptr;
398 
399  for (int i = 0; i < windowList.size(); ++i) {
400  if (closedWindow == windowList[i])
401  continue;
402 
403  QAction* action = windowActions->addAction(windowList[i]->title());
404  action->setCheckable(true);
405  action->setChecked(windowList[i] == activeWindow);
406  connect(action, &QAction::triggered, [=] { onOpenWindow(windowList[i]); });
407  windowMenu->addAction(action);
408  dockMenu->insertAction(dockLast, action);
409  }
410 
411  if (dockLast && !dockLast->isSeparator())
412  dockMenu->insertSeparator(dockLast);
413 }
414 
415 void Nexus::updateWindowsClosed()
416 {
417  updateWindowsArg(static_cast<QWidget*>(sender())->windowHandle());
418 }
419 
420 void Nexus::updateWindowsStates()
421 {
422  bool exists = false;
423  QWindowList windowList = QApplication::topLevelWindows();
424 
425  for (QWindow* window : windowList) {
426  if (!(window->windowState() & Qt::WindowMinimized)) {
427  exists = true;
428  break;
429  }
430  }
431 
432  frontAction->setEnabled(exists);
433 }
434 
435 void Nexus::onOpenWindow(QObject* object)
436 {
437  QWindow* window = static_cast<QWindow*>(object);
438 
439  if (window->windowState() & QWindow::Minimized)
440  window->showNormal();
441 
442  window->raise();
443  window->requestActivate();
444 }
445 
446 void Nexus::toggleFullscreen()
447 {
448  QWidget* window = QApplication::activeWindow();
449 
450  if (window->isFullScreen())
451  window->showNormal();
452  else
453  window->showFullScreen();
454 }
455 
456 void Nexus::bringAllToFront()
457 {
458  QWindowList windowList = QApplication::topLevelWindows();
459  QWindow* focused = QApplication::focusWindow();
460 
461  for (QWindow* window : windowList)
462  window->raise();
463 
464  focused->raise();
465 }
466 #endif
profile.h
Settings
Definition: settings.h:51
GUI::setEnabled
static void setEnabled(bool state)
Will enable or disable the GUI.
Definition: gui.cpp:69
Nexus::showLogin
int showLogin(const QString &profileName=QString())
Hides the main GUI, delete the profile, and shows the login screen.
Definition: nexus.cpp:153
Nexus::setSettings
void setSettings(Settings *settings)
Definition: nexus.cpp:194
Widget::onSelfAvatarLoaded
void onSelfAvatarLoaded(const QPixmap &pic)
Definition: widget.cpp:688
LoginScreen::loadProfile
void loadProfile(QString name, const QString &pass)
Nexus::~Nexus
~Nexus()
Definition: nexus.cpp:66
LoginScreen
Definition: loginscreen.h:33
settings.h
Profile::failedToStart
void failedToStart()
Profile::badProxy
void badProxy()
loginscreen.h
Nexus::showMainGUI
void showMainGUI()
Definition: nexus.cpp:223
Nexus::onLoadProfile
void onLoadProfile(const QString &name, const QString &pass)
Definition: nexus.cpp:309
Profile
Handles all qTox internal paths.
Definition: profile.h:42
Nexus::setProfile
void setProfile(Profile *p)
Definition: nexus.cpp:318
Settings::autoLoginChanged
void autoLoginChanged(bool enabled)
Settings::setAutoLogin
void setAutoLogin(bool state)
Definition: settings.cpp:2159
LoginScreen::autoLoginChanged
void autoLoginChanged(bool state)
Nexus::settings
Settings * settings
Definition: nexus.h:103
Nexus::saveGlobal
void saveGlobal()
Nexus::connectLoginScreen
void connectLoginScreen(const LoginScreen &loginScreen)
Definition: nexus.cpp:205
Nexus::onCreateNewProfile
void onCreateNewProfile(const QString &name, const QString &pass)
Creates a new profile and replaces the current one.
Definition: nexus.cpp:300
Nexus::getProfile
static Profile * getProfile()
Get current user profile.
Definition: nexus.cpp:290
Core::getAv
const CoreAV * getAv() const
Definition: core.cpp:703
LoginScreen::onProfileLoaded
void onProfileLoaded()
Definition: loginscreen.cpp:102
Profile::getCore
Core & getCore() const
Definition: profile.cpp:428
Nexus::profileLoadFailed
void profileLoadFailed()
Nexus::Nexus
Nexus(QObject *parent=nullptr)
Definition: nexus.cpp:60
GUI::getInstance
static GUI & getInstance()
Returns the singleton instance.
Definition: gui.cpp:56
coreav.h
Nexus::profile
Profile * profile
Definition: nexus.h:102
Nexus::currentProfileChanged
void currentProfileChanged(Profile *Profile)
Nexus::audioControl
std::unique_ptr< IAudioControl > audioControl
Definition: nexus.h:105
Widget::onCoreChanged
void onCoreChanged(Core &core)
Definition: widget.cpp:693
Widget::onBadProxyCore
void onBadProxyCore()
Definition: widget.cpp:757
camerasource.h
widget.h
groupinvite.h
Nexus::start
void start()
Sets up invariants and calls showLogin.
Definition: nexus.cpp:84
Settings::saveGlobal
void saveGlobal()
Asynchronous, saves the global settings.
Definition: settings.cpp:582
LoginScreen::createNewProfile
void createNewProfile(QString name, const QString &pass)
CoreAV::setAudio
void setAudio(IAudioControl &newAudio)
Set the audio backend.
Definition: coreav.cpp:142
LoginScreen::onProfileLoadFailed
void onProfileLoadFailed()
Definition: loginscreen.cpp:107
Nexus
Definition: nexus.h:43
LoginScreen::onAutoLoginChanged
void onAutoLoginChanged(bool state)
Definition: loginscreen.cpp:114
Nexus::bootstrapWithProfile
void bootstrapWithProfile(Profile *p)
Definition: nexus.cpp:180
Nexus::parser
QCommandLineParser * parser
Definition: nexus.h:106
Nexus::getDesktopGUI
static Widget * getDesktopGUI()
Get desktop GUI widget.
Definition: nexus.cpp:340
Profile::coreChanged
void coreChanged(Core &core)
Profile::loadProfile
static Profile * loadProfile(const QString &name, const QString &password, Settings &settings, const QCommandLineParser *parser)
Locks and loads an existing profile and creates the associate Core* instance.
Definition: profile.cpp:313
Nexus::profileLoaded
void profileLoaded()
Nexus::destroyInstance
static void destroyInstance()
Definition: nexus.cpp:267
core.h
Nexus::getInstance
static Nexus & getInstance()
Returns the singleton instance.
Definition: nexus.cpp:259
Profile::createProfile
static Profile * createProfile(const QString &name, const QString &password, Settings &settings, const QCommandLineParser *parser)
Creates a new profile and the associated Core* instance.
Definition: profile.cpp:355
gui.h
Nexus::setParser
void setParser(QCommandLineParser *parser)
Definition: nexus.cpp:331
Nexus::widget
Widget * widget
Definition: nexus.h:104
Widget::init
void init()
Definition: widget.cpp:156
Nexus::getCore
static Core * getCore()
Get core instance.
Definition: nexus.cpp:277
Profile::selfAvatarChanged
void selfAvatarChanged(const QPixmap &pixmap)
nexus.h
Widget::onFailedToStartCore
void onFailedToStartCore()
Definition: widget.cpp:747
status.h
Core
Definition: core.h:59
Widget
Definition: widget.h:87
Profile::startCore
void startCore()
Starts the Core thread.
Definition: profile.cpp:443