mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
refactor(core): cleanup Core class
- use a factory method to create it - make it handle its own thread - remove dependency on GUI
This commit is contained in:
parent
4921b8868f
commit
8574162949
|
@ -28,7 +28,6 @@
|
||||||
#include "src/model/groupinvite.h"
|
#include "src/model/groupinvite.h"
|
||||||
#include "src/nexus.h"
|
#include "src/nexus.h"
|
||||||
#include "src/persistence/profile.h"
|
#include "src/persistence/profile.h"
|
||||||
#include "src/widget/gui.h"
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
@ -40,209 +39,38 @@
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
const QString Core::TOX_EXT = ".tox";
|
const QString Core::TOX_EXT = ".tox";
|
||||||
QThread* Core::coreThread{nullptr};
|
|
||||||
|
|
||||||
#define MAX_GROUP_MESSAGE_LEN 1024
|
#define MAX_GROUP_MESSAGE_LEN 1024
|
||||||
|
|
||||||
Core::Core(QThread* CoreThread, Profile& profile, const ICoreSettings* const settings)
|
Core::Core(QThread* coreThread)
|
||||||
: tox(nullptr)
|
: tox(nullptr)
|
||||||
, av(nullptr)
|
, av(nullptr)
|
||||||
, profile(profile)
|
, coreThread{coreThread}
|
||||||
, ready(false)
|
|
||||||
, s{settings}
|
|
||||||
{
|
{
|
||||||
coreThread = CoreThread;
|
toxTimer.setSingleShot(true);
|
||||||
toxTimer = new QTimer(this);
|
connect(&this->toxTimer, &QTimer::timeout, this, &Core::process);
|
||||||
toxTimer->setSingleShot(true);
|
|
||||||
connect(toxTimer, &QTimer::timeout, this, &Core::process);
|
|
||||||
s->connectTo_dhtServerListChanged([=](const QList<DhtServer>& servers){
|
|
||||||
process();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::deadifyTox()
|
|
||||||
{
|
|
||||||
if (av) {
|
|
||||||
delete av;
|
|
||||||
av = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tox) {
|
|
||||||
tox_kill(tox);
|
|
||||||
tox = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::~Core()
|
Core::~Core()
|
||||||
{
|
{
|
||||||
if (coreThread->isRunning()) {
|
if (QThread::currentThread() == coreThread) {
|
||||||
if (QThread::currentThread() == coreThread) {
|
killTimers();
|
||||||
killTimers(false);
|
} else {
|
||||||
} else {
|
// ensure the timer is stopped, even if not called from this thread
|
||||||
QMetaObject::invokeMethod(this, "killTimers", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(this, "killTimers", Qt::BlockingQueuedConnection);
|
||||||
Q_ARG(bool, false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coreThread->exit(0);
|
coreThread->exit(0);
|
||||||
if (QThread::currentThread() != coreThread) {
|
|
||||||
while (coreThread->isRunning()) {
|
|
||||||
qApp->processEvents();
|
|
||||||
coreThread->wait(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deadifyTox();
|
delete av;
|
||||||
|
tox_kill(tox);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the global widget's Core instance
|
* @brief Registers all toxcore callbacks
|
||||||
|
* @param tox Tox instance to register the callbacks on
|
||||||
*/
|
*/
|
||||||
Core* Core::getInstance()
|
void Core::registerCallbacks(Tox * tox) {
|
||||||
{
|
|
||||||
return Nexus::getCore();
|
|
||||||
}
|
|
||||||
|
|
||||||
const CoreAV* Core::getAv() const
|
|
||||||
{
|
|
||||||
return av;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreAV* Core::getAv()
|
|
||||||
{
|
|
||||||
return av;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates Tox instance from previously saved data
|
|
||||||
* @param savedata Previously saved Tox data - null, if new profile was created
|
|
||||||
*/
|
|
||||||
void Core::makeTox(QByteArray savedata)
|
|
||||||
{
|
|
||||||
auto toxOptions = ToxOptions::makeToxOptions(savedata, s);
|
|
||||||
if (toxOptions == nullptr) {
|
|
||||||
qCritical() << "could not allocate Tox Options data structure";
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TOX_ERR_NEW tox_err;
|
|
||||||
tox = tox_new(*toxOptions, &tox_err);
|
|
||||||
|
|
||||||
switch (tox_err) {
|
|
||||||
case TOX_ERR_NEW_OK:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TOX_ERR_NEW_LOAD_BAD_FORMAT:
|
|
||||||
qCritical() << "failed to parse Tox save data";
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case TOX_ERR_NEW_PORT_ALLOC:
|
|
||||||
if (s->getEnableIPv6()) {
|
|
||||||
tox_options_set_ipv6_enabled(*toxOptions, false);
|
|
||||||
tox = tox_new(*toxOptions, &tox_err);
|
|
||||||
if (tox_err == TOX_ERR_NEW_OK) {
|
|
||||||
qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery "
|
|
||||||
"may not work properly.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qCritical() << "can't to bind the port";
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case TOX_ERR_NEW_PROXY_BAD_HOST:
|
|
||||||
case TOX_ERR_NEW_PROXY_BAD_PORT:
|
|
||||||
case TOX_ERR_NEW_PROXY_BAD_TYPE:
|
|
||||||
qCritical() << "bad proxy, error code:" << tox_err;
|
|
||||||
emit badProxy();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case TOX_ERR_NEW_PROXY_NOT_FOUND:
|
|
||||||
qCritical() << "proxy not found";
|
|
||||||
emit badProxy();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case TOX_ERR_NEW_LOAD_ENCRYPTED:
|
|
||||||
qCritical() << "attempted to load encrypted Tox save data";
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case TOX_ERR_NEW_MALLOC:
|
|
||||||
qCritical() << "memory allocation failed";
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case TOX_ERR_NEW_NULL:
|
|
||||||
qCritical() << "a parameter was null";
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
qCritical() << "Tox core failed to start, unknown error code:" << tox_err;
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initializes the core, must be called before anything else
|
|
||||||
*/
|
|
||||||
void Core::start(const QByteArray& savedata)
|
|
||||||
{
|
|
||||||
bool isNewProfile = profile.isNewProfile();
|
|
||||||
if (isNewProfile) {
|
|
||||||
qDebug() << "Creating a new profile";
|
|
||||||
makeTox(QByteArray());
|
|
||||||
setStatusMessage(tr("Toxing on qTox"));
|
|
||||||
setUsername(profile.getName());
|
|
||||||
} else {
|
|
||||||
qDebug() << "Loading user profile";
|
|
||||||
if (savedata.isEmpty()) {
|
|
||||||
emit failedToStart();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
makeTox(savedata);
|
|
||||||
}
|
|
||||||
|
|
||||||
qsrand(time(nullptr));
|
|
||||||
if (!tox) {
|
|
||||||
ready = true;
|
|
||||||
GUI::setEnabled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// toxcore is successfully created, create toxav
|
|
||||||
av = new CoreAV(tox);
|
|
||||||
if (!av->getToxAv()) {
|
|
||||||
qCritical() << "Toxav failed to start";
|
|
||||||
emit failedToStart();
|
|
||||||
deadifyTox();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set GUI with user and statusmsg
|
|
||||||
QString name = getUsername();
|
|
||||||
if (!name.isEmpty()) {
|
|
||||||
emit usernameSet(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString msg = getStatusMessage();
|
|
||||||
if (!msg.isEmpty()) {
|
|
||||||
emit statusMessageSet(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
ToxId id = getSelfId();
|
|
||||||
// TODO: probably useless check, comes basically directly from toxcore
|
|
||||||
if (id.isValid()) {
|
|
||||||
emit idSet(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadFriends();
|
|
||||||
|
|
||||||
tox_callback_friend_request(tox, onFriendRequest);
|
tox_callback_friend_request(tox, onFriendRequest);
|
||||||
tox_callback_friend_message(tox, onFriendMessage);
|
tox_callback_friend_message(tox, onFriendMessage);
|
||||||
tox_callback_friend_name(tox, onFriendNameChange);
|
tox_callback_friend_name(tox, onFriendNameChange);
|
||||||
|
@ -264,22 +92,176 @@ void Core::start(const QByteArray& savedata)
|
||||||
tox_callback_file_recv(tox, CoreFile::onFileReceiveCallback);
|
tox_callback_file_recv(tox, CoreFile::onFileReceiveCallback);
|
||||||
tox_callback_file_recv_chunk(tox, CoreFile::onFileRecvChunkCallback);
|
tox_callback_file_recv_chunk(tox, CoreFile::onFileRecvChunkCallback);
|
||||||
tox_callback_file_recv_control(tox, CoreFile::onFileControlCallback);
|
tox_callback_file_recv_control(tox, CoreFile::onFileControlCallback);
|
||||||
|
}
|
||||||
|
|
||||||
ready = true;
|
/**
|
||||||
|
* @brief Factory method for the Core object
|
||||||
|
* @param savedata empty if new profile or saved data else
|
||||||
|
* @param settings Settings specific to Core
|
||||||
|
* @return nullptr or a Core object ready to start
|
||||||
|
*/
|
||||||
|
ToxCorePtr Core::makeToxCore(const QByteArray &savedata, const ICoreSettings * const settings)
|
||||||
|
{
|
||||||
|
QThread* thread = new QThread();
|
||||||
|
if (thread == nullptr) {
|
||||||
|
qCritical() << "could not allocate Core thread";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
thread->setObjectName("qTox Core");
|
||||||
|
|
||||||
if (isNewProfile) {
|
auto toxOptions = ToxOptions::makeToxOptions(savedata, settings);
|
||||||
profile.saveToxSave();
|
if (toxOptions == nullptr) {
|
||||||
|
qCritical() << "could not allocate Tox Options data structure";
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isReady()) {
|
ToxCorePtr core(new Core(thread));
|
||||||
GUI::setEnabled(true);
|
if(core == nullptr) {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TOX_ERR_NEW tox_err;
|
||||||
|
core->tox = tox_new(*toxOptions, &tox_err);
|
||||||
|
|
||||||
|
switch (tox_err) {
|
||||||
|
case TOX_ERR_NEW_OK:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_ERR_NEW_LOAD_BAD_FORMAT:
|
||||||
|
qCritical() << "failed to parse Tox save data";
|
||||||
|
return {};
|
||||||
|
|
||||||
|
case TOX_ERR_NEW_PORT_ALLOC:
|
||||||
|
if (toxOptions->getIPv6Enabled()) {
|
||||||
|
toxOptions->setIPv6Enabled(false);
|
||||||
|
core->tox = tox_new(*toxOptions, &tox_err);
|
||||||
|
if (tox_err == TOX_ERR_NEW_OK) {
|
||||||
|
qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery "
|
||||||
|
"may not work properly.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qCritical() << "can't to bind the port";
|
||||||
|
return {};
|
||||||
|
|
||||||
|
case TOX_ERR_NEW_PROXY_BAD_HOST:
|
||||||
|
case TOX_ERR_NEW_PROXY_BAD_PORT:
|
||||||
|
case TOX_ERR_NEW_PROXY_BAD_TYPE:
|
||||||
|
qCritical() << "bad proxy, error code:" << tox_err;
|
||||||
|
//emit badProxy();
|
||||||
|
return {};
|
||||||
|
|
||||||
|
case TOX_ERR_NEW_PROXY_NOT_FOUND:
|
||||||
|
qCritical() << "proxy not found";
|
||||||
|
//emit badProxy();
|
||||||
|
return {};
|
||||||
|
|
||||||
|
case TOX_ERR_NEW_LOAD_ENCRYPTED:
|
||||||
|
qCritical() << "attempted to load encrypted Tox save data";
|
||||||
|
//emit failedToStart();
|
||||||
|
return {};
|
||||||
|
|
||||||
|
case TOX_ERR_NEW_MALLOC:
|
||||||
|
qCritical() << "memory allocation failed";
|
||||||
|
//emit failedToStart();
|
||||||
|
return {};
|
||||||
|
|
||||||
|
case TOX_ERR_NEW_NULL:
|
||||||
|
qCritical() << "a parameter was null";
|
||||||
|
//emit failedToStart();
|
||||||
|
return {};
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCritical() << "Tox core failed to start, unknown error code:" << tox_err;
|
||||||
|
//emit failedToStart();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
qsrand(time(nullptr)); // TODO(sudden6): needed?
|
||||||
|
// tox should be valid by now
|
||||||
|
assert(core->tox != nullptr);
|
||||||
|
|
||||||
|
// toxcore is successfully created, create toxav
|
||||||
|
core->av = new CoreAV(core->tox);
|
||||||
|
if (!core->av || !core->av->getToxAv()) {
|
||||||
|
qCritical() << "Toxav failed to start";
|
||||||
|
//emit failedToStart();
|
||||||
|
//deadifyTox();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
registerCallbacks(core->tox);
|
||||||
|
|
||||||
|
// connect the thread with the Core
|
||||||
|
connect(thread, &QThread::started, core.get(), &Core::onStarted);
|
||||||
|
core->moveToThread(thread);
|
||||||
|
// since this is allocated in the constructor move it to the other thread too
|
||||||
|
core->toxTimer.moveToThread(thread);
|
||||||
|
|
||||||
|
// when leaving this function 'core' should be ready for it's start() action or
|
||||||
|
// a nullptr
|
||||||
|
return core;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::onStarted()
|
||||||
|
{
|
||||||
|
// TODO(sudden6): this assert should hold?
|
||||||
|
//assert(QThread::currentThread() == coreThread);
|
||||||
|
// One time initialization stuff
|
||||||
|
// set GUI with user and statusmsg
|
||||||
|
QString name = getUsername();
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
emit usernameSet(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString msg = getStatusMessage();
|
||||||
|
if (!msg.isEmpty()) {
|
||||||
|
emit statusMessageSet(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxId id = getSelfId();
|
||||||
|
// TODO: probably useless check, comes basically directly from toxcore
|
||||||
|
if (id.isValid()) {
|
||||||
|
emit idSet(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFriends();
|
||||||
|
|
||||||
process(); // starts its own timer
|
process(); // starts its own timer
|
||||||
av->start();
|
av->start();
|
||||||
emit avReady();
|
emit avReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Starts toxcore and it's event loop, must be run from the Core thread
|
||||||
|
* @return true on success, false otherwise
|
||||||
|
*/
|
||||||
|
bool Core::start()
|
||||||
|
{
|
||||||
|
coreThread->start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the global widget's Core instance
|
||||||
|
*/
|
||||||
|
Core* Core::getInstance()
|
||||||
|
{
|
||||||
|
return Nexus::getCore();
|
||||||
|
}
|
||||||
|
|
||||||
|
const CoreAV* Core::getAv() const
|
||||||
|
{
|
||||||
|
return av;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreAV* Core::getAv()
|
||||||
|
{
|
||||||
|
return av;
|
||||||
|
}
|
||||||
|
|
||||||
/* Using the now commented out statements in checkConnection(), I watched how
|
/* Using the now commented out statements in checkConnection(), I watched how
|
||||||
* many ticks disconnects-after-initial-connect lasted. Out of roughly 15 trials,
|
* many ticks disconnects-after-initial-connect lasted. Out of roughly 15 trials,
|
||||||
* 5 disconnected; 4 were DCd for less than 20 ticks, while the 5th was ~50 ticks.
|
* 5 disconnected; 4 were DCd for less than 20 ticks, while the 5th was ~50 ticks.
|
||||||
|
@ -294,19 +276,21 @@ void Core::start(const QByteArray& savedata)
|
||||||
*/
|
*/
|
||||||
void Core::process()
|
void Core::process()
|
||||||
{
|
{
|
||||||
|
assert(QThread::currentThread() == coreThread);
|
||||||
if (!isReady()) {
|
if (!isReady()) {
|
||||||
av->stop();
|
av->stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tolerance = CORE_DISCONNECT_TOLERANCE;
|
static int tolerance = CORE_DISCONNECT_TOLERANCE;
|
||||||
tox_iterate(tox, getInstance());
|
tox_iterate(tox, this);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// we want to see the debug messages immediately
|
// we want to see the debug messages immediately
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO(sudden6): recheck if this is still necessary
|
||||||
if (checkConnection()) {
|
if (checkConnection()) {
|
||||||
tolerance = CORE_DISCONNECT_TOLERANCE;
|
tolerance = CORE_DISCONNECT_TOLERANCE;
|
||||||
} else if (!(--tolerance)) {
|
} else if (!(--tolerance)) {
|
||||||
|
@ -315,7 +299,7 @@ void Core::process()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned sleeptime = qMin(tox_iteration_interval(tox), CoreFile::corefileIterationInterval());
|
unsigned sleeptime = qMin(tox_iteration_interval(tox), CoreFile::corefileIterationInterval());
|
||||||
toxTimer->start(sleeptime);
|
toxTimer.start(sleeptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::checkConnection()
|
bool Core::checkConnection()
|
||||||
|
@ -339,7 +323,8 @@ bool Core::checkConnection()
|
||||||
*/
|
*/
|
||||||
void Core::bootstrapDht()
|
void Core::bootstrapDht()
|
||||||
{
|
{
|
||||||
QList<DhtServer> dhtServerList = s->getDhtServerList();
|
// TODO(sudden6): fix bootstrapping
|
||||||
|
QList<DhtServer> dhtServerList{};// = s->getDhtServerList();
|
||||||
int listSize = dhtServerList.size();
|
int listSize = dhtServerList.size();
|
||||||
if (!listSize) {
|
if (!listSize) {
|
||||||
qWarning() << "no bootstrap list?!?";
|
qWarning() << "no bootstrap list?!?";
|
||||||
|
@ -540,7 +525,8 @@ void Core::acceptFriendRequest(const ToxPk& friendPk)
|
||||||
if (friendId == std::numeric_limits<uint32_t>::max()) {
|
if (friendId == std::numeric_limits<uint32_t>::max()) {
|
||||||
emit failedToAddFriend(friendPk);
|
emit failedToAddFriend(friendPk);
|
||||||
} else {
|
} else {
|
||||||
profile.saveToxSave();
|
// TODO(sudden6): emit save request
|
||||||
|
//profile.saveToxSave();
|
||||||
emit friendAdded(friendId, friendPk);
|
emit friendAdded(friendId, friendPk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -579,7 +565,8 @@ void Core::requestFriendship(const ToxId& friendId, const QString& message)
|
||||||
QString errorMessage = getFriendRequestErrorMessage(friendId, message);
|
QString errorMessage = getFriendRequestErrorMessage(friendId, message);
|
||||||
if (!errorMessage.isNull()) {
|
if (!errorMessage.isNull()) {
|
||||||
emit failedToAddFriend(friendPk, errorMessage);
|
emit failedToAddFriend(friendPk, errorMessage);
|
||||||
profile.saveToxSave();
|
// TODO(sudden6): emit save request
|
||||||
|
// profile.saveToxSave();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +582,8 @@ void Core::requestFriendship(const ToxId& friendId, const QString& message)
|
||||||
emit requestSent(friendPk, message);
|
emit requestSent(friendPk, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.saveToxSave();
|
// TODO(sudden6): emit save request
|
||||||
|
// profile.saveToxSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Core::sendMessage(uint32_t friendId, const QString& message)
|
int Core::sendMessage(uint32_t friendId, const QString& message)
|
||||||
|
@ -757,8 +745,8 @@ void Core::removeFriend(uint32_t friendId, bool fake)
|
||||||
emit failedToRemoveFriend(friendId);
|
emit failedToRemoveFriend(friendId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO(sudden6): emit save request
|
||||||
profile.saveToxSave();
|
// profile.saveToxSave();
|
||||||
emit friendRemoved(friendId);
|
emit friendRemoved(friendId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -817,9 +805,8 @@ void Core::setUsername(const QString& username)
|
||||||
}
|
}
|
||||||
|
|
||||||
emit usernameSet(username);
|
emit usernameSet(username);
|
||||||
if (ready) {
|
|
||||||
profile.saveToxSave();
|
// TODO(sudden6): request saving
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -900,9 +887,7 @@ void Core::setStatusMessage(const QString& message)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ready) {
|
// TODO(sudden6): request saving
|
||||||
profile.saveToxSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit statusMessageSet(message);
|
emit statusMessageSet(message);
|
||||||
}
|
}
|
||||||
|
@ -929,7 +914,8 @@ void Core::setStatus(Status status)
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_self_set_status(tox, userstatus);
|
tox_self_set_status(tox, userstatus);
|
||||||
profile.saveToxSave();
|
// TODO(sudden6): emit save request
|
||||||
|
// profile.saveToxSave();
|
||||||
emit statusSet(status);
|
emit statusSet(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1369,7 +1355,7 @@ QString Core::getPeerName(const ToxPk& id) const
|
||||||
*/
|
*/
|
||||||
bool Core::isReady() const
|
bool Core::isReady() const
|
||||||
{
|
{
|
||||||
return av && av->getToxAv() && tox && ready;
|
return av && av->getToxAv() && tox;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1383,32 +1369,13 @@ void Core::setNospam(uint32_t nospam)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the unencrypted tox save data
|
* @brief Stops the AV thread and the timer here
|
||||||
*/
|
*/
|
||||||
void Core::killTimers(bool onlyStop)
|
void Core::killTimers()
|
||||||
{
|
{
|
||||||
assert(QThread::currentThread() == coreThread);
|
assert(QThread::currentThread() == coreThread);
|
||||||
if (av) {
|
if (av) {
|
||||||
av->stop();
|
av->stop();
|
||||||
}
|
}
|
||||||
toxTimer->stop();
|
toxTimer.stop();
|
||||||
if (!onlyStop) {
|
|
||||||
delete toxTimer;
|
|
||||||
toxTimer = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reinitialized the core.
|
|
||||||
* @warning Must be called from the Core thread, with the GUI thread ready to process events.
|
|
||||||
*/
|
|
||||||
void Core::reset()
|
|
||||||
{
|
|
||||||
assert(QThread::currentThread() == coreThread);
|
|
||||||
QByteArray toxsave = getToxSaveData();
|
|
||||||
ready = false;
|
|
||||||
killTimers(true);
|
|
||||||
deadifyTox();
|
|
||||||
GUI::clearContacts();
|
|
||||||
start(toxsave);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,10 @@
|
||||||
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class CoreAV;
|
class CoreAV;
|
||||||
class ICoreSettings;
|
class ICoreSettings;
|
||||||
|
@ -45,11 +47,16 @@ enum class Status
|
||||||
Offline
|
Offline
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Core;
|
||||||
|
|
||||||
|
using ToxCorePtr = std::unique_ptr<Core>;
|
||||||
|
|
||||||
class Core : public QObject
|
class Core : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Core(QThread* coreThread, Profile& profile, const ICoreSettings* const settings);
|
|
||||||
|
static ToxCorePtr makeToxCore(const QByteArray& savedata, const ICoreSettings* const settings);
|
||||||
static Core* getInstance();
|
static Core* getInstance();
|
||||||
const CoreAV* getAv() const;
|
const CoreAV* getAv() const;
|
||||||
CoreAV* getAv();
|
CoreAV* getAv();
|
||||||
|
@ -85,10 +92,7 @@ public:
|
||||||
void sendFile(uint32_t friendId, QString filename, QString filePath, long long filesize);
|
void sendFile(uint32_t friendId, QString filename, QString filePath, long long filesize);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start(const QByteArray& savedata);
|
bool start();
|
||||||
void reset();
|
|
||||||
void process();
|
|
||||||
void bootstrapDht();
|
|
||||||
|
|
||||||
QByteArray getToxSaveData();
|
QByteArray getToxSaveData();
|
||||||
|
|
||||||
|
@ -189,6 +193,8 @@ signals:
|
||||||
void fileSendFailed(uint32_t friendId, const QString& fname);
|
void fileSendFailed(uint32_t friendId, const QString& fname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Core(QThread* coreThread);
|
||||||
|
|
||||||
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage,
|
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage,
|
||||||
size_t cMessageSize, void* core);
|
size_t cMessageSize, void* core);
|
||||||
static void onFriendMessage(Tox* tox, uint32_t friendId, TOX_MESSAGE_TYPE type,
|
static void onFriendMessage(Tox* tox, uint32_t friendId, TOX_MESSAGE_TYPE type,
|
||||||
|
@ -224,28 +230,28 @@ private:
|
||||||
bool checkConnection();
|
bool checkConnection();
|
||||||
|
|
||||||
void checkEncryptedHistory();
|
void checkEncryptedHistory();
|
||||||
void makeTox(QByteArray savedata);
|
void makeTox(QByteArray savedata, ICoreSettings *s);
|
||||||
void makeAv();
|
void makeAv();
|
||||||
void loadFriends();
|
void loadFriends();
|
||||||
|
void bootstrapDht();
|
||||||
|
|
||||||
void checkLastOnline(uint32_t friendId);
|
void checkLastOnline(uint32_t friendId);
|
||||||
|
|
||||||
void deadifyTox();
|
|
||||||
QString getFriendRequestErrorMessage(const ToxId& friendId, const QString& message) const;
|
QString getFriendRequestErrorMessage(const ToxId& friendId, const QString& message) const;
|
||||||
|
static void registerCallbacks(Tox * tox);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void killTimers(bool onlyStop);
|
void killTimers();
|
||||||
|
void process();
|
||||||
|
void onStarted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Tox* tox;
|
Tox* tox;
|
||||||
CoreAV* av;
|
CoreAV* av;
|
||||||
QTimer* toxTimer;
|
QTimer toxTimer;
|
||||||
Profile& profile;
|
|
||||||
QMutex messageSendMutex;
|
QMutex messageSendMutex;
|
||||||
bool ready;
|
|
||||||
const ICoreSettings* const s;
|
|
||||||
|
|
||||||
static QThread* coreThread;
|
QThread* coreThread = nullptr;
|
||||||
|
|
||||||
friend class Audio; ///< Audio can access our calls directly to reduce latency
|
friend class Audio; ///< Audio can access our calls directly to reduce latency
|
||||||
friend class CoreFile; ///< CoreFile can access tox* and emit our signals
|
friend class CoreFile; ///< CoreFile can access tox* and emit our signals
|
||||||
|
|
|
@ -296,15 +296,17 @@ void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, u
|
||||||
qDebug() << QString("Received empty avatar request %1:%2").arg(friendId).arg(fileId);
|
qDebug() << QString("Received empty avatar request %1:%2").arg(friendId).arg(fileId);
|
||||||
// Avatars of size 0 means explicitely no avatar
|
// Avatars of size 0 means explicitely no avatar
|
||||||
emit core->friendAvatarRemoved(friendId);
|
emit core->friendAvatarRemoved(friendId);
|
||||||
core->profile.removeAvatar(friendPk);
|
// TODO(sudden6): use signal from above for that action
|
||||||
|
//core->profile.removeAvatar(friendPk);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH,
|
static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH,
|
||||||
"TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!");
|
"TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!");
|
||||||
uint8_t avatarHash[TOX_FILE_ID_LENGTH];
|
uint8_t avatarHash[TOX_FILE_ID_LENGTH];
|
||||||
tox_file_get_file_id(core->tox, friendId, fileId, avatarHash, nullptr);
|
tox_file_get_file_id(core->tox, friendId, fileId, avatarHash, nullptr);
|
||||||
if (core->profile.getAvatarHash(friendPk)
|
// TODO(sudden6): fix that condition below
|
||||||
== QByteArray((char*)avatarHash, TOX_HASH_LENGTH)) {
|
if (/*core->profile.getAvatarHash(friendPk)
|
||||||
|
== QByteArray((char*)avatarHash, TOX_HASH_LENGTH)*/ false) {
|
||||||
// If it's an avatar but we already have it cached, cancel
|
// If it's an avatar but we already have it cached, cancel
|
||||||
qDebug() << QString(
|
qDebug() << QString(
|
||||||
"Received avatar request %1:%2, reject, since we have it in cache.")
|
"Received avatar request %1:%2, reject, since we have it in cache.")
|
||||||
|
@ -446,8 +448,10 @@ void CoreFile::onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fil
|
||||||
pic.loadFromData(file->avatarData);
|
pic.loadFromData(file->avatarData);
|
||||||
if (!pic.isNull()) {
|
if (!pic.isNull()) {
|
||||||
qDebug() << "Got" << file->avatarData.size() << "bytes of avatar data from" << friendId;
|
qDebug() << "Got" << file->avatarData.size() << "bytes of avatar data from" << friendId;
|
||||||
|
// TODO(sudden6): handle the action below with the signal
|
||||||
|
/*
|
||||||
core->profile.saveAvatar(file->avatarData,
|
core->profile.saveAvatar(file->avatarData,
|
||||||
core->getFriendPublicKey(friendId));
|
core->getFriendPublicKey(friendId));*/
|
||||||
emit core->friendAvatarChanged(friendId, pic);
|
emit core->friendAvatarChanged(friendId, pic);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -115,3 +115,14 @@ std::unique_ptr<ToxOptions> ToxOptions::makeToxOptions(const QByteArray& savedat
|
||||||
|
|
||||||
return toxOptions;
|
return toxOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ToxOptions::getIPv6Enabled() const
|
||||||
|
{
|
||||||
|
return tox_options_get_ipv6_enabled(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToxOptions::setIPv6Enabled(bool enabled)
|
||||||
|
{
|
||||||
|
tox_options_set_ipv6_enabled(options, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@ public:
|
||||||
operator Tox_Options* ();
|
operator Tox_Options* ();
|
||||||
const char* getProxyAddrData() const;
|
const char* getProxyAddrData() const;
|
||||||
static std::unique_ptr<ToxOptions> makeToxOptions(const QByteArray &savedata, const ICoreSettings *s);
|
static std::unique_ptr<ToxOptions> makeToxOptions(const QByteArray &savedata, const ICoreSettings *s);
|
||||||
|
bool getIPv6Enabled() const;
|
||||||
|
void setIPv6Enabled(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ToxOptions(Tox_Options *options, const QByteArray& proxyAddrData);
|
ToxOptions(Tox_Options *options, const QByteArray& proxyAddrData);
|
||||||
|
|
|
@ -224,6 +224,8 @@ void Nexus::showMainGUI()
|
||||||
connect(widget, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
|
connect(widget, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
|
||||||
|
|
||||||
profile->startCore();
|
profile->startCore();
|
||||||
|
|
||||||
|
GUI::setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -60,30 +60,22 @@ Profile::Profile(QString name, const QString& password, bool isNewProfile, const
|
||||||
s.setCurrentProfile(name);
|
s.setCurrentProfile(name);
|
||||||
s.saveGlobal();
|
s.saveGlobal();
|
||||||
|
|
||||||
coreThread = new QThread();
|
core = Core::makeToxCore(toxsave, &s);
|
||||||
coreThread->setObjectName("qTox Core");
|
if(!core) {
|
||||||
core = new Core(coreThread, *this, &Settings::getInstance());
|
qDebug() << "failed to start ToxCore";
|
||||||
QObject::connect(core, &Core::idSet, this,
|
return;
|
||||||
[this, password](const ToxId& id) { loadDatabase(id, password); },
|
}
|
||||||
Qt::QueuedConnection);
|
|
||||||
core->moveToThread(coreThread);
|
|
||||||
QObject::connect(coreThread, &QThread::started, core, [=]() {
|
|
||||||
core->start(toxsave);
|
|
||||||
|
|
||||||
// prevent segfault by checking if core started successfully
|
const ToxId& selfId = core->getSelfId();
|
||||||
if(!core->isReady()) {
|
loadDatabase(selfId, password);
|
||||||
qWarning() << "Core not ready, aborting";
|
const ToxPk& selfPk = selfId.getPublicKey();
|
||||||
return;
|
QByteArray data = loadAvatarData(selfPk);
|
||||||
}
|
if (data.isEmpty()) {
|
||||||
|
qDebug() << "Self avatar not found, will broadcast empty avatar to friends";
|
||||||
|
}
|
||||||
|
|
||||||
const ToxPk selfPk = core->getSelfPublicKey();
|
// TODO(sudden6): check if needed
|
||||||
QByteArray data = loadAvatarData(selfPk);
|
//setAvatar(data, selfPk);
|
||||||
if (data.isEmpty()) {
|
|
||||||
qDebug() << "Self avatar not found, will broadcast empty avatar to friends";
|
|
||||||
}
|
|
||||||
|
|
||||||
setAvatar(data, selfPk);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -223,11 +215,6 @@ Profile::~Profile()
|
||||||
saveToxSave();
|
saveToxSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
core->deleteLater();
|
|
||||||
while (coreThread->isRunning())
|
|
||||||
qApp->processEvents();
|
|
||||||
|
|
||||||
delete coreThread;
|
|
||||||
if (!isRemoved) {
|
if (!isRemoved) {
|
||||||
Settings::getInstance().savePersonal(this);
|
Settings::getInstance().savePersonal(this);
|
||||||
Settings::getInstance().sync();
|
Settings::getInstance().sync();
|
||||||
|
@ -281,7 +268,8 @@ QStringList Profile::getProfiles()
|
||||||
|
|
||||||
Core* Profile::getCore()
|
Core* Profile::getCore()
|
||||||
{
|
{
|
||||||
return core;
|
// TODO(sudden6): this is evil
|
||||||
|
return core.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Profile::getName() const
|
QString Profile::getName() const
|
||||||
|
@ -294,7 +282,7 @@ QString Profile::getName() const
|
||||||
*/
|
*/
|
||||||
void Profile::startCore()
|
void Profile::startCore()
|
||||||
{
|
{
|
||||||
coreThread->start();
|
core->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Profile::isNewProfile()
|
bool Profile::isNewProfile()
|
||||||
|
@ -721,12 +709,13 @@ const ToxEncrypt* Profile::getPasskey() const
|
||||||
*/
|
*/
|
||||||
void Profile::restartCore()
|
void Profile::restartCore()
|
||||||
{
|
{
|
||||||
|
/* TODO(sudden6): rethink this
|
||||||
GUI::setEnabled(false); // Core::reset re-enables it
|
GUI::setEnabled(false); // Core::reset re-enables it
|
||||||
if (!isRemoved && core->isReady()) {
|
if (!isRemoved && core->isReady()) {
|
||||||
saveToxSave();
|
saveToxSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(core, "reset");
|
QMetaObject::invokeMethod(core, "reset");
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#ifndef PROFILE_H
|
#ifndef PROFILE_H
|
||||||
#define PROFILE_H
|
#define PROFILE_H
|
||||||
|
|
||||||
|
#include "src/core/core.h"
|
||||||
#include "src/core/toxencrypt.h"
|
#include "src/core/toxencrypt.h"
|
||||||
#include "src/core/toxid.h"
|
#include "src/core/toxid.h"
|
||||||
|
|
||||||
|
@ -33,9 +34,6 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class Core;
|
|
||||||
class QThread;
|
|
||||||
|
|
||||||
class Profile : public QObject
|
class Profile : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -96,8 +94,7 @@ private:
|
||||||
QString avatarPath(const ToxPk& owner, bool forceUnencrypted = false);
|
QString avatarPath(const ToxPk& owner, bool forceUnencrypted = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core* core;
|
std::unique_ptr<Core> core = nullptr;
|
||||||
QThread* coreThread;
|
|
||||||
QString name;
|
QString name;
|
||||||
std::unique_ptr<ToxEncrypt> passkey = nullptr;
|
std::unique_ptr<ToxEncrypt> passkey = nullptr;
|
||||||
std::shared_ptr<RawDatabase> database;
|
std::shared_ptr<RawDatabase> database;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user