mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Clean up callback hell between AV UI and CoreAV
This commit is contained in:
parent
a505e06f83
commit
477554ffba
|
@ -63,6 +63,7 @@ CoreAV::~CoreAV()
|
||||||
{
|
{
|
||||||
for (const ToxFriendCall& call : calls)
|
for (const ToxFriendCall& call : calls)
|
||||||
cancelCall(call.callId);
|
cancelCall(call.callId);
|
||||||
|
stop();
|
||||||
toxav_kill(toxav);
|
toxav_kill(toxav);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,56 +99,82 @@ bool CoreAV::anyActiveCalls()
|
||||||
return !calls.isEmpty();
|
return !calls.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::answerCall(uint32_t friendNum)
|
bool CoreAV::isCallVideoEnabled(uint32_t friendNum)
|
||||||
{
|
{
|
||||||
|
assert(calls.contains(friendNum));
|
||||||
|
return calls[friendNum].videoEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoreAV::answerCall(uint32_t friendNum)
|
||||||
|
{
|
||||||
|
if (QThread::currentThread() != coreavThread.get())
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
QMetaObject::invokeMethod(this, "answerCall", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(bool, ret), Q_ARG(uint32_t, friendNum));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << QString("answering call %1").arg(friendNum);
|
qDebug() << QString("answering call %1").arg(friendNum);
|
||||||
assert(calls.contains(friendNum));
|
assert(calls.contains(friendNum));
|
||||||
TOXAV_ERR_ANSWER err;
|
TOXAV_ERR_ANSWER err;
|
||||||
if (toxav_answer(toxav, friendNum, AUDIO_DEFAULT_BITRATE, VIDEO_DEFAULT_BITRATE, &err))
|
if (toxav_answer(toxav, friendNum, AUDIO_DEFAULT_BITRATE, VIDEO_DEFAULT_BITRATE, &err))
|
||||||
{
|
{
|
||||||
calls[friendNum].inactive = false;
|
calls[friendNum].inactive = false;
|
||||||
emit avStart(friendNum, calls[friendNum].videoEnabled);
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qWarning() << "Failed to answer call with error"<<err;
|
qWarning() << "Failed to answer call with error"<<err;
|
||||||
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
|
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
|
||||||
calls.remove(friendNum);
|
calls.remove(friendNum);
|
||||||
emit avCallFailed(friendNum);
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::startCall(uint32_t friendId, bool video)
|
bool CoreAV::startCall(uint32_t friendNum, bool video)
|
||||||
{
|
{
|
||||||
qDebug() << QString("Starting call with %1").arg(friendId);
|
if (QThread::currentThread() != coreavThread.get())
|
||||||
if(calls.contains(friendId))
|
|
||||||
{
|
{
|
||||||
qWarning() << QString("Can't start call with %1, we're already in this call!").arg(friendId);
|
bool ret;
|
||||||
emit avCallFailed(friendId);
|
(void)QMetaObject::invokeMethod(this, "startCall", Qt::BlockingQueuedConnection,
|
||||||
return;
|
Q_RETURN_ARG(bool, ret), Q_ARG(uint32_t, friendNum), Q_ARG(bool, video));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << QString("Starting call with %1").arg(friendNum);
|
||||||
|
if(calls.contains(friendNum))
|
||||||
|
{
|
||||||
|
qWarning() << QString("Can't start call with %1, we're already in this call!").arg(friendNum);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0;
|
uint32_t videoBitrate = video ? VIDEO_DEFAULT_BITRATE : 0;
|
||||||
if (!toxav_call(toxav, friendId, AUDIO_DEFAULT_BITRATE, videoBitrate, nullptr))
|
if (!toxav_call(toxav, friendNum, AUDIO_DEFAULT_BITRATE, videoBitrate, nullptr))
|
||||||
{
|
return false;
|
||||||
emit avCallFailed(friendId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
calls.insert({friendId, video, *this});
|
calls.insert({friendNum, video, *this});
|
||||||
emit avRinging(friendId, video);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::cancelCall(uint32_t friendId)
|
bool CoreAV::cancelCall(uint32_t friendNum)
|
||||||
{
|
{
|
||||||
qDebug() << QString("Cancelling call with %1").arg(friendId);
|
if (QThread::currentThread() != coreavThread.get())
|
||||||
if (!toxav_call_control(toxav, friendId, TOXAV_CALL_CONTROL_CANCEL, nullptr))
|
|
||||||
{
|
{
|
||||||
qWarning() << QString("Failed to cancel call with %1").arg(friendId);
|
bool ret;
|
||||||
return;
|
(void)QMetaObject::invokeMethod(this, "cancelCall", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(bool, ret), Q_ARG(uint32_t, friendNum));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
calls.remove(friendId);
|
|
||||||
emit avCancel(friendId);
|
qDebug() << QString("Cancelling call with %1").arg(friendNum);
|
||||||
|
if (!toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr))
|
||||||
|
{
|
||||||
|
qWarning() << QString("Failed to cancel call with %1").arg(friendNum);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
calls.remove(friendNum);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreAV::sendCallAudio(uint32_t callId)
|
bool CoreAV::sendCallAudio(uint32_t callId)
|
||||||
|
@ -357,12 +384,22 @@ void CoreAV::resetCallSources()
|
||||||
void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool video, void *_self)
|
void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool video, void *_self)
|
||||||
{
|
{
|
||||||
CoreAV* self = static_cast<CoreAV*>(_self);
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
||||||
|
|
||||||
|
// Run this slow path callback asynchronously on the AV thread to avoid deadlocks
|
||||||
|
if (QThread::currentThread() != self->coreavThread.get())
|
||||||
|
{
|
||||||
|
return (void)QMetaObject::invokeMethod(self, "callCallback", Qt::QueuedConnection,
|
||||||
|
Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum),
|
||||||
|
Q_ARG(bool, audio), Q_ARG(bool, video), Q_ARG(void*, _self));
|
||||||
|
}
|
||||||
|
|
||||||
if (self->calls.contains(friendNum))
|
if (self->calls.contains(friendNum))
|
||||||
{
|
{
|
||||||
qWarning() << QString("Rejecting call invite from %1, we're already in that call!").arg(friendNum);
|
qWarning() << QString("Rejecting call invite from %1, we're already in that call!").arg(friendNum);
|
||||||
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
|
toxav_call_control(toxav, friendNum, TOXAV_CALL_CONTROL_CANCEL, nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
qDebug() << QString("Received call invite from %1").arg(friendNum);
|
||||||
const auto& callIt = self->calls.insert({friendNum, video, *self});
|
const auto& callIt = self->calls.insert({friendNum, video, *self});
|
||||||
|
|
||||||
// We don't get a state callback when answering, so fill the state ourselves in advance
|
// We don't get a state callback when answering, so fill the state ourselves in advance
|
||||||
|
@ -376,26 +413,34 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
|
||||||
emit reinterpret_cast<CoreAV*>(self)->avInvite(friendNum, video);
|
emit reinterpret_cast<CoreAV*>(self)->avInvite(friendNum, video);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *_self)
|
void CoreAV::stateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t state, void *_self)
|
||||||
{
|
{
|
||||||
// This callback needs to run in the CoreAV thread.
|
|
||||||
// Otherwise, there's a deadlock between the Core thread holding an internal
|
|
||||||
// toxav lock and trying to lock the CameraSource to stop it, and the
|
|
||||||
// av thread holding the Camera
|
|
||||||
|
|
||||||
CoreAV* self = static_cast<CoreAV*>(_self);
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
||||||
|
|
||||||
assert(self->calls.contains(friendNum));
|
// Run this slow path callback asynchronously on the AV thread to avoid deadlocks
|
||||||
|
if (QThread::currentThread() != self->coreavThread.get())
|
||||||
|
{
|
||||||
|
return (void)QMetaObject::invokeMethod(self, "stateCallback", Qt::QueuedConnection,
|
||||||
|
Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum),
|
||||||
|
Q_ARG(uint32_t, state), Q_ARG(void*, _self));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!self->calls.contains(friendNum))
|
||||||
|
{
|
||||||
|
qWarning() << QString("stateCallback called, but call %1 is already dead").arg(friendNum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ToxFriendCall& call = self->calls[friendNum];
|
ToxFriendCall& call = self->calls[friendNum];
|
||||||
|
|
||||||
if (state & TOXAV_FRIEND_CALL_STATE_ERROR)
|
if (state & TOXAV_FRIEND_CALL_STATE_ERROR)
|
||||||
{
|
{
|
||||||
qWarning() << "Call with friend"<<friendNum<<"died of unnatural causes!";
|
qWarning() << "Call with friend"<<friendNum<<"died of unnatural causes!";
|
||||||
calls.remove(friendNum);
|
calls.remove(friendNum);
|
||||||
emit self->avCallFailed(friendNum);
|
emit self->avEnd(friendNum);
|
||||||
}
|
}
|
||||||
else if (state & TOXAV_FRIEND_CALL_STATE_FINISHED)
|
else if (state & TOXAV_FRIEND_CALL_STATE_FINISHED)
|
||||||
{
|
{
|
||||||
|
qDebug() << "Call with friend"<<friendNum<<"finished quietly";
|
||||||
calls.remove(friendNum);
|
calls.remove(friendNum);
|
||||||
emit self->avEnd(friendNum);
|
emit self->avEnd(friendNum);
|
||||||
}
|
}
|
||||||
|
@ -412,13 +457,33 @@ void CoreAV::stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void *_s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::audioBitrateCallback(ToxAV*, uint32_t friendNum, bool stable, uint32_t rate, void *)
|
void CoreAV::audioBitrateCallback(ToxAV* toxav, uint32_t friendNum, bool stable, uint32_t rate, void *_self)
|
||||||
{
|
{
|
||||||
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
||||||
|
|
||||||
|
// Run this slow path callback asynchronously on the AV thread to avoid deadlocks
|
||||||
|
if (QThread::currentThread() != self->coreavThread.get())
|
||||||
|
{
|
||||||
|
return (void)QMetaObject::invokeMethod(self, "audioBitrateCallback", Qt::QueuedConnection,
|
||||||
|
Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum),
|
||||||
|
Q_ARG(bool, stable), Q_ARG(uint32_t, rate), Q_ARG(void*, _self));
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "Audio bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
|
qDebug() << "Audio bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreAV::videoBitrateCallback(ToxAV*, uint32_t friendNum, bool stable, uint32_t rate, void *)
|
void CoreAV::videoBitrateCallback(ToxAV* toxav, uint32_t friendNum, bool stable, uint32_t rate, void *_self)
|
||||||
{
|
{
|
||||||
|
CoreAV* self = static_cast<CoreAV*>(_self);
|
||||||
|
|
||||||
|
// Run this slow path callback asynchronously on the AV thread to avoid deadlocks
|
||||||
|
if (QThread::currentThread() != self->coreavThread.get())
|
||||||
|
{
|
||||||
|
return (void)QMetaObject::invokeMethod(self, "videoBitrateCallback", Qt::QueuedConnection,
|
||||||
|
Q_ARG(ToxAV*, toxav), Q_ARG(uint32_t, friendNum),
|
||||||
|
Q_ARG(bool, stable), Q_ARG(uint32_t, rate), Q_ARG(void*, _self));
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "Video bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
|
qDebug() << "Video bitrate with"<<friendNum<<" is now "<<rate<<", stability:"<<stable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,62 +59,54 @@ public:
|
||||||
const ToxAV* getToxAv() const;
|
const ToxAV* getToxAv() const;
|
||||||
|
|
||||||
bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
|
bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active)
|
||||||
void prepareCall(uint32_t friendId, ToxAV *toxav, bool videoEnabled);
|
bool isCallVideoEnabled(uint32_t friendNum);
|
||||||
void cleanupCall(uint32_t friendId);
|
bool sendCallAudio(uint32_t friendNum); ///< Returns false only on error, but not if there's nothing to send
|
||||||
bool sendCallAudio(uint32_t friendId); ///< Returns false only on error, but not if there's nothing to send
|
void sendCallVideo(uint32_t friendNum, std::shared_ptr<VideoFrame> frame);
|
||||||
void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
|
bool sendGroupCallAudio(int groupNum);
|
||||||
unsigned channels, int sampleRate);
|
|
||||||
void sendCallVideo(uint32_t friendId, std::shared_ptr<VideoFrame> frame);
|
|
||||||
bool sendGroupCallAudio(int groupId);
|
|
||||||
|
|
||||||
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source
|
||||||
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
||||||
|
|
||||||
void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
|
void joinGroupCall(int groupNum); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
|
||||||
void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call. Call from the GUI thread.
|
void leaveGroupCall(int groupNum); ///< Will not leave the group, just stop the call. Call from the GUI thread.
|
||||||
void disableGroupCallMic(int groupId);
|
void disableGroupCallMic(int groupNum);
|
||||||
void disableGroupCallVol(int groupId);
|
void disableGroupCallVol(int groupNum);
|
||||||
void enableGroupCallMic(int groupId);
|
void enableGroupCallMic(int groupNum);
|
||||||
void enableGroupCallVol(int groupId);
|
void enableGroupCallVol(int groupNum);
|
||||||
bool isGroupCallMicEnabled(int groupId) const;
|
bool isGroupCallMicEnabled(int groupNum) const;
|
||||||
bool isGroupCallVolEnabled(int groupId) const;
|
bool isGroupCallVolEnabled(int groupNum) const;
|
||||||
bool isGroupAvEnabled(int groupId) const; ///< True for AV groups, false for text-only groups
|
bool isGroupAvEnabled(int groupNum) const; ///< True for AV groups, false for text-only groups
|
||||||
|
|
||||||
void startCall(uint32_t friendId, bool video=false);
|
void micMuteToggle(uint32_t friendNum);
|
||||||
void answerCall(uint32_t friendId);
|
void volMuteToggle(uint32_t friendNum);
|
||||||
void cancelCall(uint32_t friendId);
|
|
||||||
|
|
||||||
void micMuteToggle(uint32_t friendId);
|
|
||||||
void volMuteToggle(uint32_t friendId);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
bool startCall(uint32_t friendNum, bool video=false);
|
||||||
|
bool answerCall(uint32_t friendNum);
|
||||||
|
bool cancelCall(uint32_t friendNum);
|
||||||
|
/// Starts the CoreAV main loop that calls toxav's main loop
|
||||||
void start();
|
void start();
|
||||||
|
/// Stops the main loop
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void avInvite(uint32_t friendId, bool video);
|
void avInvite(uint32_t friendId, bool video); ///< Sent when a friend calls us
|
||||||
void avStart(uint32_t friendId, bool video);
|
void avStart(uint32_t friendId, bool video); ///< Sent when a call we initiated has started
|
||||||
void avCancel(uint32_t friendId);
|
void avEnd(uint32_t friendId); ///< Sent when a call was ended by the peer
|
||||||
void avEnd(uint32_t friendId);
|
|
||||||
void avRinging(uint32_t friendId, bool video);
|
|
||||||
void avCallFailed(uint32_t friendId);
|
|
||||||
|
|
||||||
void videoFrameReceived(vpx_image* frame);
|
private slots:
|
||||||
|
static void callCallback(ToxAV *toxAV, uint32_t friendNum, bool audio, bool video, void* self);
|
||||||
|
static void stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void* self);
|
||||||
|
static void audioBitrateCallback(ToxAV *toxAV, uint32_t friendNum, bool stable, uint32_t rate, void* self);
|
||||||
|
static void videoBitrateCallback(ToxAV *toxAV, uint32_t friendNum, bool stable, uint32_t rate, void* self);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process();
|
void process();
|
||||||
static void callCallback(ToxAV *toxAV, uint32_t friendNum, bool audio, bool video, void* self);
|
|
||||||
static void stateCallback(ToxAV *toxAV, uint32_t friendNum, uint32_t state, void* self);
|
|
||||||
static void audioBitrateCallback(ToxAV *toxAV, uint32_t friendNum, bool stable, uint32_t rate, void* self);
|
|
||||||
static void videoBitrateCallback(ToxAV *toxAV, uint32_t friendNum, bool stable, uint32_t rate, void* self);
|
|
||||||
static void audioFrameCallback(ToxAV *toxAV, uint32_t friendNum, const int16_t *pcm, size_t sampleCount,
|
static void audioFrameCallback(ToxAV *toxAV, uint32_t friendNum, const int16_t *pcm, size_t sampleCount,
|
||||||
uint8_t channels, uint32_t samplingRate, void* self);
|
uint8_t channels, uint32_t samplingRate, void* self);
|
||||||
static void videoFrameCallback(ToxAV *toxAV, uint32_t friendNum, uint16_t w, uint16_t h,
|
static void videoFrameCallback(ToxAV *toxAV, uint32_t friendNum, uint16_t w, uint16_t h,
|
||||||
const uint8_t *y, const uint8_t *u, const uint8_t *v,
|
const uint8_t *y, const uint8_t *u, const uint8_t *v,
|
||||||
int32_t ystride, int32_t ustride, int32_t vstride, void* self);
|
int32_t ystride, int32_t ustride, int32_t vstride, void* self);
|
||||||
/// Intercepts a function call and moves it to another thread
|
|
||||||
/// Useful to move callbacks from the toxcore thread to our thread
|
|
||||||
template <class... Args> void asyncTransplantThunk(void(*fun)(Args...), Args... args);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint32_t AUDIO_DEFAULT_BITRATE = 64; ///< In kb/s. More than enough for Opus.
|
static constexpr uint32_t AUDIO_DEFAULT_BITRATE = 64; ///< In kb/s. More than enough for Opus.
|
||||||
|
|
|
@ -17,6 +17,8 @@ ToxCall::ToxCall(uint32_t CallId)
|
||||||
: sendAudioTimer{new QTimer}, callId{CallId},
|
: sendAudioTimer{new QTimer}, callId{CallId},
|
||||||
inactive{true}, muteMic{false}, muteVol{false}, alSource{0}
|
inactive{true}, muteMic{false}, muteVol{false}, alSource{0}
|
||||||
{
|
{
|
||||||
|
qCritical() << "CREATED CALL "<<callId;
|
||||||
|
|
||||||
sendAudioTimer->setInterval(5);
|
sendAudioTimer->setInterval(5);
|
||||||
sendAudioTimer->setSingleShot(true);
|
sendAudioTimer->setSingleShot(true);
|
||||||
|
|
||||||
|
@ -52,6 +54,9 @@ ToxCall::ToxCall(ToxCall&& other)
|
||||||
|
|
||||||
ToxCall::~ToxCall()
|
ToxCall::~ToxCall()
|
||||||
{
|
{
|
||||||
|
if (callId != (uint32_t)(int32_t)-1)
|
||||||
|
qCritical() << "DELETED CALL "<<callId;
|
||||||
|
|
||||||
if (sendAudioTimer)
|
if (sendAudioTimer)
|
||||||
{
|
{
|
||||||
QObject::disconnect(sendAudioTimer, nullptr, nullptr, nullptr);
|
QObject::disconnect(sendAudioTimer, nullptr, nullptr, nullptr);
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
#include <QSignalMapper>
|
#include <QSignalMapper>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Q_DECLARE_OPAQUE_POINTER(ToxAV*)
|
||||||
|
|
||||||
static Nexus* nexus{nullptr};
|
static Nexus* nexus{nullptr};
|
||||||
|
|
||||||
Nexus::Nexus(QObject *parent) :
|
Nexus::Nexus(QObject *parent) :
|
||||||
|
@ -90,6 +92,7 @@ void Nexus::start()
|
||||||
qRegisterMetaType<int64_t>("int64_t");
|
qRegisterMetaType<int64_t>("int64_t");
|
||||||
qRegisterMetaType<QPixmap>("QPixmap");
|
qRegisterMetaType<QPixmap>("QPixmap");
|
||||||
qRegisterMetaType<Profile*>("Profile*");
|
qRegisterMetaType<Profile*>("Profile*");
|
||||||
|
qRegisterMetaType<ToxAV*>("ToxAV*");
|
||||||
qRegisterMetaType<ToxFile>("ToxFile");
|
qRegisterMetaType<ToxFile>("ToxFile");
|
||||||
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
||||||
qRegisterMetaType<std::shared_ptr<VideoFrame>>("std::shared_ptr<VideoFrame>");
|
qRegisterMetaType<std::shared_ptr<VideoFrame>>("std::shared_ptr<VideoFrame>");
|
||||||
|
|
|
@ -65,6 +65,9 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||||
: f(chatFriend)
|
: f(chatFriend)
|
||||||
, isTyping{false}
|
, isTyping{false}
|
||||||
{
|
{
|
||||||
|
Core* core = Core::getInstance();
|
||||||
|
coreav = core->getAv();
|
||||||
|
|
||||||
nameLabel->setText(f->getDisplayedName());
|
nameLabel->setText(f->getDisplayedName());
|
||||||
|
|
||||||
avatar->setPixmap(QPixmap(":/img/contact_dark.svg"), Qt::transparent);
|
avatar->setPixmap(QPixmap(":/img/contact_dark.svg"), Qt::transparent);
|
||||||
|
@ -96,7 +99,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||||
|
|
||||||
loadHistoryAction = menu.addAction(QString(), this, SLOT(onLoadHistory()));
|
loadHistoryAction = menu.addAction(QString(), this, SLOT(onLoadHistory()));
|
||||||
|
|
||||||
connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
connect(core, &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
||||||
connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered);
|
connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered);
|
||||||
connect(fileButton, &QPushButton::clicked, this, &ChatForm::onAttachClicked);
|
connect(fileButton, &QPushButton::clicked, this, &ChatForm::onAttachClicked);
|
||||||
connect(screenshotButton, &QPushButton::clicked, this, &ChatForm::onScreenshotClicked);
|
connect(screenshotButton, &QPushButton::clicked, this, &ChatForm::onScreenshotClicked);
|
||||||
|
@ -104,7 +107,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||||
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
|
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
|
||||||
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
||||||
connect(msgEdit, &ChatTextEdit::textChanged, this, &ChatForm::onTextEditChanged);
|
connect(msgEdit, &ChatTextEdit::textChanged, this, &ChatForm::onTextEditChanged);
|
||||||
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
|
connect(core, &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
|
||||||
connect(this, &ChatForm::chatAreaCleared, getOfflineMsgEngine(), &OfflineMsgEngine::removeAllReciepts);
|
connect(this, &ChatForm::chatAreaCleared, getOfflineMsgEngine(), &OfflineMsgEngine::removeAllReciepts);
|
||||||
connect(&typingTimer, &QTimer::timeout, this, [=]{
|
connect(&typingTimer, &QTimer::timeout, this, [=]{
|
||||||
Core::getInstance()->sendTyping(f->getFriendID(), false);
|
Core::getInstance()->sendTyping(f->getFriendID(), false);
|
||||||
|
@ -262,37 +265,26 @@ void ChatForm::onAvInvite(uint32_t FriendId, bool video)
|
||||||
if (video)
|
if (video)
|
||||||
{
|
{
|
||||||
callConfirm = new CallConfirmWidget(videoButton, *f);
|
callConfirm = new CallConfirmWidget(videoButton, *f);
|
||||||
if (f->getFriendWidget()->chatFormIsSet(false))
|
|
||||||
callConfirm->show();
|
|
||||||
|
|
||||||
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
|
|
||||||
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
|
|
||||||
|
|
||||||
callButton->setObjectName("grey");
|
|
||||||
callButton->setToolTip("");
|
|
||||||
videoButton->setObjectName("yellow");
|
videoButton->setObjectName("yellow");
|
||||||
videoButton->setToolTip(tr("Accept video call"));
|
videoButton->setToolTip(tr("Accept video call"));
|
||||||
|
videoButton->style()->polish(videoButton);
|
||||||
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered);
|
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callConfirm = new CallConfirmWidget(callButton, *f);
|
callConfirm = new CallConfirmWidget(callButton, *f);
|
||||||
|
callButton->setObjectName("yellow");
|
||||||
|
callButton->setToolTip(tr("Accept audio call"));
|
||||||
|
callButton->style()->polish(callButton);
|
||||||
|
connect(callButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered);
|
||||||
|
}
|
||||||
|
|
||||||
if (f->getFriendWidget()->chatFormIsSet(false))
|
if (f->getFriendWidget()->chatFormIsSet(false))
|
||||||
callConfirm->show();
|
callConfirm->show();
|
||||||
|
|
||||||
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
|
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
|
||||||
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
|
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
|
||||||
|
|
||||||
callButton->setObjectName("yellow");
|
|
||||||
callButton->setToolTip(tr("Accept audio call"));
|
|
||||||
videoButton->setObjectName("grey");
|
|
||||||
videoButton->setToolTip("");
|
|
||||||
connect(callButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered);
|
|
||||||
}
|
|
||||||
|
|
||||||
callButton->style()->polish(callButton);
|
|
||||||
videoButton->style()->polish(videoButton);
|
|
||||||
|
|
||||||
insertChatMessage(ChatMessage::createChatInfoMessage(tr("%1 calling").arg(f->getDisplayedName()),
|
insertChatMessage(ChatMessage::createChatInfoMessage(tr("%1 calling").arg(f->getDisplayedName()),
|
||||||
ChatMessage::INFO,
|
ChatMessage::INFO,
|
||||||
QDateTime::currentDateTime()));
|
QDateTime::currentDateTime()));
|
||||||
|
@ -346,26 +338,6 @@ void ChatForm::onAvStart(uint32_t FriendId, bool video)
|
||||||
startCounter();
|
startCounter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onAvCancel(uint32_t FriendId)
|
|
||||||
{
|
|
||||||
if (FriendId != f->getFriendID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
qDebug() << "onAvCancel";
|
|
||||||
|
|
||||||
delete callConfirm;
|
|
||||||
callConfirm = nullptr;
|
|
||||||
|
|
||||||
enableCallButtons();
|
|
||||||
stopCounter();
|
|
||||||
|
|
||||||
hideNetcam();
|
|
||||||
|
|
||||||
addSystemInfoMessage(tr("%1 stopped calling").arg(f->getDisplayedName()),
|
|
||||||
ChatMessage::INFO,
|
|
||||||
QDateTime::currentDateTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::onAvEnd(uint32_t FriendId)
|
void ChatForm::onAvEnd(uint32_t FriendId)
|
||||||
{
|
{
|
||||||
if (FriendId != f->getFriendID())
|
if (FriendId != f->getFriendID())
|
||||||
|
@ -381,12 +353,10 @@ void ChatForm::onAvEnd(uint32_t FriendId)
|
||||||
hideNetcam();
|
hideNetcam();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onAvRinging(uint32_t FriendId, bool video)
|
void ChatForm::showOutgoingCall(bool video)
|
||||||
{
|
{
|
||||||
if (FriendId != f->getFriendID())
|
audioInputFlag = true;
|
||||||
return;
|
audioOutputFlag = true;
|
||||||
|
|
||||||
qDebug() << "onAvRinging";
|
|
||||||
|
|
||||||
disableCallButtons();
|
disableCallButtons();
|
||||||
if (video)
|
if (video)
|
||||||
|
@ -394,16 +364,16 @@ void ChatForm::onAvRinging(uint32_t FriendId, bool video)
|
||||||
videoButton->setObjectName("yellow");
|
videoButton->setObjectName("yellow");
|
||||||
videoButton->style()->polish(videoButton);
|
videoButton->style()->polish(videoButton);
|
||||||
videoButton->setToolTip(tr("Cancel video call"));
|
videoButton->setToolTip(tr("Cancel video call"));
|
||||||
connect(videoButton, SIGNAL(clicked()),
|
connect(videoButton, &QPushButton::clicked,
|
||||||
this, SLOT(onCancelCallTriggered()));
|
this, &ChatForm::onCancelCallTriggered);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callButton->setObjectName("yellow");
|
callButton->setObjectName("yellow");
|
||||||
callButton->style()->polish(callButton);
|
callButton->style()->polish(callButton);
|
||||||
callButton->setToolTip(tr("Cancel audio call"));
|
callButton->setToolTip(tr("Cancel audio call"));
|
||||||
connect(callButton, SIGNAL(clicked()),
|
connect(callButton, &QPushButton::clicked,
|
||||||
this, SLOT(onCancelCallTriggered()));
|
this, &ChatForm::onCancelCallTriggered);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSystemInfoMessage(tr("Calling %1").arg(f->getDisplayedName()),
|
addSystemInfoMessage(tr("Calling %1").arg(f->getDisplayedName()),
|
||||||
|
@ -425,9 +395,15 @@ void ChatForm::onAnswerCallTriggered()
|
||||||
|
|
||||||
disableCallButtons();
|
disableCallButtons();
|
||||||
|
|
||||||
audioInputFlag = true;
|
if (!coreav->answerCall(f->getFriendID()))
|
||||||
audioOutputFlag = true;
|
{
|
||||||
emit answerCall(f->getFriendID());
|
enableCallButtons();
|
||||||
|
stopCounter();
|
||||||
|
hideNetcam();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onAvStart(f->getFriendID(), coreav->isCallVideoEnabled(f->getFriendID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onHangupCallTriggered()
|
void ChatForm::onHangupCallTriggered()
|
||||||
|
@ -440,10 +416,11 @@ void ChatForm::onHangupCallTriggered()
|
||||||
|
|
||||||
audioInputFlag = false;
|
audioInputFlag = false;
|
||||||
audioOutputFlag = false;
|
audioOutputFlag = false;
|
||||||
emit hangupCall(f->getFriendID());
|
coreav->cancelCall(f->getFriendID());
|
||||||
|
|
||||||
stopCounter();
|
stopCounter();
|
||||||
enableCallButtons();
|
enableCallButtons();
|
||||||
|
hideNetcam();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onRejectCallTriggered()
|
void ChatForm::onRejectCallTriggered()
|
||||||
|
@ -458,7 +435,7 @@ void ChatForm::onRejectCallTriggered()
|
||||||
|
|
||||||
audioInputFlag = false;
|
audioInputFlag = false;
|
||||||
audioOutputFlag = false;
|
audioOutputFlag = false;
|
||||||
emit rejectCall(f->getFriendID());
|
coreav->cancelCall(f->getFriendID());
|
||||||
|
|
||||||
enableCallButtons();
|
enableCallButtons();
|
||||||
stopCounter();
|
stopCounter();
|
||||||
|
@ -468,45 +445,30 @@ void ChatForm::onCallTriggered()
|
||||||
{
|
{
|
||||||
qDebug() << "onCallTriggered";
|
qDebug() << "onCallTriggered";
|
||||||
|
|
||||||
audioInputFlag = true;
|
|
||||||
audioOutputFlag = true;
|
|
||||||
disableCallButtons();
|
disableCallButtons();
|
||||||
emit startCall(f->getFriendID(), false);
|
if (coreav->startCall(f->getFriendID(), false))
|
||||||
|
showOutgoingCall(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onVideoCallTriggered()
|
void ChatForm::onVideoCallTriggered()
|
||||||
{
|
{
|
||||||
qDebug() << "onVideoCallTriggered";
|
qDebug() << "onVideoCallTriggered";
|
||||||
|
|
||||||
audioInputFlag = true;
|
|
||||||
audioOutputFlag = true;
|
|
||||||
disableCallButtons();
|
disableCallButtons();
|
||||||
emit startCall(f->getFriendID(), true);
|
if (coreav->startCall(f->getFriendID(), true))
|
||||||
}
|
showOutgoingCall(false);
|
||||||
|
|
||||||
void ChatForm::onAvCallFailed(uint32_t FriendId)
|
|
||||||
{
|
|
||||||
if (FriendId != f->getFriendID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
qDebug() << "onAvCallFailed";
|
|
||||||
|
|
||||||
delete callConfirm;
|
|
||||||
callConfirm = nullptr;
|
|
||||||
|
|
||||||
enableCallButtons();
|
|
||||||
stopCounter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onCancelCallTriggered()
|
void ChatForm::onCancelCallTriggered()
|
||||||
{
|
{
|
||||||
qDebug() << "onCancelCallTriggered";
|
qDebug() << "onCancelCallTriggered";
|
||||||
|
|
||||||
|
if (!coreav->cancelCall(f->getFriendID()))
|
||||||
|
qWarning() << "Failed to cancel a call! Assuming we're not in call";
|
||||||
|
|
||||||
enableCallButtons();
|
enableCallButtons();
|
||||||
stopCounter();
|
stopCounter();
|
||||||
|
|
||||||
hideNetcam();
|
hideNetcam();
|
||||||
emit cancelCall(f->getFriendID());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::enableCallButtons()
|
void ChatForm::enableCallButtons()
|
||||||
|
@ -587,7 +549,7 @@ void ChatForm::onMicMuteToggle()
|
||||||
{
|
{
|
||||||
if (audioInputFlag == true)
|
if (audioInputFlag == true)
|
||||||
{
|
{
|
||||||
emit micMuteToggle(f->getFriendID());
|
coreav->micMuteToggle(f->getFriendID());
|
||||||
if (micButton->objectName() == "red")
|
if (micButton->objectName() == "red")
|
||||||
{
|
{
|
||||||
micButton->setObjectName("green");
|
micButton->setObjectName("green");
|
||||||
|
@ -607,7 +569,7 @@ void ChatForm::onVolMuteToggle()
|
||||||
{
|
{
|
||||||
if (audioOutputFlag == true)
|
if (audioOutputFlag == true)
|
||||||
{
|
{
|
||||||
emit volMuteToggle(f->getFriendID());
|
coreav->volMuteToggle(f->getFriendID());
|
||||||
if (volButton->objectName() == "red")
|
if (volButton->objectName() == "red")
|
||||||
{
|
{
|
||||||
volButton->setObjectName("green");
|
volButton->setObjectName("green");
|
||||||
|
|
|
@ -34,6 +34,7 @@ class CallConfirmWidget;
|
||||||
class QHideEvent;
|
class QHideEvent;
|
||||||
class QMoveEvent;
|
class QMoveEvent;
|
||||||
class OfflineMsgEngine;
|
class OfflineMsgEngine;
|
||||||
|
class CoreAV;
|
||||||
|
|
||||||
class ChatForm : public GenericChatForm
|
class ChatForm : public GenericChatForm
|
||||||
{
|
{
|
||||||
|
@ -52,13 +53,6 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendFile(uint32_t friendId, QString, QString, long long);
|
void sendFile(uint32_t friendId, QString, QString, long long);
|
||||||
void startCall(uint32_t friendId, bool video);
|
|
||||||
void answerCall(uint32_t friendId);
|
|
||||||
void hangupCall(uint32_t friendId);
|
|
||||||
void cancelCall(uint32_t friendId);
|
|
||||||
void rejectCall(uint32_t friendId);
|
|
||||||
void micMuteToggle(uint32_t friendId);
|
|
||||||
void volMuteToggle(uint32_t friendId);
|
|
||||||
void aliasChanged(const QString& alias);
|
void aliasChanged(const QString& alias);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -66,12 +60,7 @@ public slots:
|
||||||
void onFileRecvRequest(ToxFile file);
|
void onFileRecvRequest(ToxFile file);
|
||||||
void onAvInvite(uint32_t FriendId, bool video);
|
void onAvInvite(uint32_t FriendId, bool video);
|
||||||
void onAvStart(uint32_t FriendId, bool video);
|
void onAvStart(uint32_t FriendId, bool video);
|
||||||
void onAvCancel(uint32_t FriendId);
|
|
||||||
void onAvEnd(uint32_t FriendId);
|
void onAvEnd(uint32_t FriendId);
|
||||||
void onAvRinging(uint32_t FriendId, bool video);
|
|
||||||
void onAvCallFailed(uint32_t FriendId);
|
|
||||||
void onMicMuteToggle();
|
|
||||||
void onVolMuteToggle();
|
|
||||||
void onAvatarChange(uint32_t FriendId, const QPixmap& pic);
|
void onAvatarChange(uint32_t FriendId, const QPixmap& pic);
|
||||||
void onAvatarRemoved(uint32_t FriendId);
|
void onAvatarRemoved(uint32_t FriendId);
|
||||||
|
|
||||||
|
@ -85,6 +74,8 @@ private slots:
|
||||||
void onHangupCallTriggered();
|
void onHangupCallTriggered();
|
||||||
void onCancelCallTriggered();
|
void onCancelCallTriggered();
|
||||||
void onRejectCallTriggered();
|
void onRejectCallTriggered();
|
||||||
|
void onMicMuteToggle();
|
||||||
|
void onVolMuteToggle();
|
||||||
void onFileSendFailed(uint32_t FriendId, const QString &fname);
|
void onFileSendFailed(uint32_t FriendId, const QString &fname);
|
||||||
void onLoadHistory();
|
void onLoadHistory();
|
||||||
void onUpdateTime();
|
void onUpdateTime();
|
||||||
|
@ -96,6 +87,7 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
void showOutgoingCall(bool video);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual GenericNetCamView* createNetcam() final override;
|
virtual GenericNetCamView* createNetcam() final override;
|
||||||
|
@ -106,6 +98,7 @@ protected:
|
||||||
virtual void showEvent(QShowEvent* event) final override;
|
virtual void showEvent(QShowEvent* event) final override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
CoreAV* coreav;
|
||||||
Friend* f;
|
Friend* f;
|
||||||
CroppingLabel *statusMessageLabel;
|
CroppingLabel *statusMessageLabel;
|
||||||
QLabel *callDuration;
|
QLabel *callDuration;
|
||||||
|
|
|
@ -892,21 +892,11 @@ void Widget::addFriend(int friendId, const QString &userId)
|
||||||
connect(newfriend->getChatForm(), &GenericChatForm::sendMessage, core, &Core::sendMessage);
|
connect(newfriend->getChatForm(), &GenericChatForm::sendMessage, core, &Core::sendMessage);
|
||||||
connect(newfriend->getChatForm(), &GenericChatForm::sendAction, core, &Core::sendAction);
|
connect(newfriend->getChatForm(), &GenericChatForm::sendAction, core, &Core::sendAction);
|
||||||
connect(newfriend->getChatForm(), &ChatForm::sendFile, core, &Core::sendFile);
|
connect(newfriend->getChatForm(), &ChatForm::sendFile, core, &Core::sendFile);
|
||||||
connect(newfriend->getChatForm(), &ChatForm::answerCall, core->getAv(), &CoreAV::answerCall);
|
|
||||||
connect(newfriend->getChatForm(), &ChatForm::hangupCall, core->getAv(), &CoreAV::cancelCall);
|
|
||||||
connect(newfriend->getChatForm(), &ChatForm::rejectCall, core->getAv(), &CoreAV::cancelCall);
|
|
||||||
connect(newfriend->getChatForm(), &ChatForm::startCall, core->getAv(), &CoreAV::startCall);
|
|
||||||
connect(newfriend->getChatForm(), &ChatForm::cancelCall, core->getAv(), &CoreAV::cancelCall);
|
|
||||||
connect(newfriend->getChatForm(), &ChatForm::micMuteToggle, core->getAv(), &CoreAV::micMuteToggle);
|
|
||||||
connect(newfriend->getChatForm(), &ChatForm::volMuteToggle, core->getAv(), &CoreAV::volMuteToggle);
|
|
||||||
connect(newfriend->getChatForm(), &ChatForm::aliasChanged, newfriend->getFriendWidget(), &FriendWidget::setAlias);
|
connect(newfriend->getChatForm(), &ChatForm::aliasChanged, newfriend->getFriendWidget(), &FriendWidget::setAlias);
|
||||||
connect(core, &Core::fileReceiveRequested, newfriend->getChatForm(), &ChatForm::onFileRecvRequest);
|
connect(core, &Core::fileReceiveRequested, newfriend->getChatForm(), &ChatForm::onFileRecvRequest);
|
||||||
connect(coreav, &CoreAV::avInvite, newfriend->getChatForm(), &ChatForm::onAvInvite);
|
connect(coreav, &CoreAV::avInvite, newfriend->getChatForm(), &ChatForm::onAvInvite, Qt::BlockingQueuedConnection);
|
||||||
connect(coreav, &CoreAV::avStart, newfriend->getChatForm(), &ChatForm::onAvStart);
|
connect(coreav, &CoreAV::avStart, newfriend->getChatForm(), &ChatForm::onAvStart, Qt::BlockingQueuedConnection);
|
||||||
connect(coreav, &CoreAV::avCancel, newfriend->getChatForm(), &ChatForm::onAvCancel);
|
connect(coreav, &CoreAV::avEnd, newfriend->getChatForm(), &ChatForm::onAvEnd, Qt::BlockingQueuedConnection);
|
||||||
connect(coreav, &CoreAV::avEnd, newfriend->getChatForm(), &ChatForm::onAvEnd);
|
|
||||||
connect(coreav, &CoreAV::avRinging, newfriend->getChatForm(), &ChatForm::onAvRinging);
|
|
||||||
connect(coreav, &CoreAV::avCallFailed, newfriend->getChatForm(), &ChatForm::onAvCallFailed);
|
|
||||||
connect(core, &Core::friendAvatarChanged, newfriend->getChatForm(), &ChatForm::onAvatarChange);
|
connect(core, &Core::friendAvatarChanged, newfriend->getChatForm(), &ChatForm::onAvatarChange);
|
||||||
connect(core, &Core::friendAvatarChanged, newfriend->getFriendWidget(), &FriendWidget::onAvatarChange);
|
connect(core, &Core::friendAvatarChanged, newfriend->getFriendWidget(), &FriendWidget::onAvatarChange);
|
||||||
connect(core, &Core::friendAvatarRemoved, newfriend->getChatForm(), &ChatForm::onAvatarRemoved);
|
connect(core, &Core::friendAvatarRemoved, newfriend->getChatForm(), &ChatForm::onAvatarRemoved);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user