mumble-voip_mumble/src/mumble/ClientUser.cpp

328 lines
8.5 KiB
C++

// Copyright 2009-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "ClientUser.h"
#include "AudioOutput.h"
#include "Channel.h"
#include "PluginManager.h"
#include "Global.h"
QHash< unsigned int, ClientUser * > ClientUser::c_qmUsers;
QReadWriteLock ClientUser::c_qrwlUsers;
QList< ClientUser * > ClientUser::c_qlTalking;
QReadWriteLock ClientUser::c_qrwlTalking;
ClientUser::ClientUser(QObject *p)
: QObject(p), tsState(Settings::Passive), tLastTalkStateChange(false), bLocalIgnore(false), bLocalIgnoreTTS(false),
bLocalMute(false), fPowerMin(0.0f), fPowerMax(0.0f), fAverageAvailable(0.0f), iFrames(0), iSequence(0) {
}
float ClientUser::getLocalVolumeAdjustments() const {
return m_localVolume;
}
QString ClientUser::getLocalNickname() const {
return m_localNickname;
}
ClientUser *ClientUser::get(unsigned int uiSession) {
QReadLocker lock(&c_qrwlUsers);
ClientUser *p = c_qmUsers.value(uiSession);
return p;
}
QList< ClientUser * > ClientUser::getTalking() {
QReadLocker lock(&c_qrwlTalking);
return c_qlTalking;
}
QList< ClientUser * > ClientUser::getActive() {
QReadLocker lock(&c_qrwlUsers);
QList< ClientUser * > activeUsers;
foreach (ClientUser *cu, c_qmUsers) {
if (cu->isActive())
activeUsers << cu;
}
return activeUsers;
}
bool ClientUser::isValid(unsigned int uiSession) {
QReadLocker lock(&c_qrwlUsers);
return c_qmUsers.contains(uiSession);
}
ClientUser *ClientUser::add(unsigned int uiSession, QObject *po) {
QWriteLocker lock(&c_qrwlUsers);
ClientUser *p = new ClientUser(po);
p->uiSession = uiSession;
c_qmUsers[uiSession] = p;
QObject::connect(p, &ClientUser::talkingStateChanged, Global::get().pluginManager,
&PluginManager::on_userTalkingStateChanged);
return p;
}
ClientUser *ClientUser::match(const ClientUser *other, bool matchname) {
QReadLocker lock(&c_qrwlUsers);
ClientUser *p;
foreach (p, c_qmUsers) {
if (p == other)
continue;
if ((p->iId >= 0) && (p->iId == other->iId))
return p;
if (matchname && (p->qsName == other->qsName))
return p;
}
return nullptr;
}
void ClientUser::remove(unsigned int uiSession) {
ClientUser *p;
{
QWriteLocker lock(&c_qrwlUsers);
p = c_qmUsers.take(uiSession);
if (p) {
if (p->cChannel)
p->cChannel->removeUser(p);
if (p->tsState != Settings::Passive) {
QWriteLocker writeLock(&c_qrwlTalking);
c_qlTalking.removeAll(p);
}
}
}
if (p) {
AudioOutputPtr ao = Global::get().ao;
if (ao) {
// It is safe to call this function and to give the ClientUser pointer
// to it even though we don't hold the lock anymore as it will only take
// the pointer to use as the key in a HashMap lookup. At no point in the
// code triggered by this function call will the ClientUser pointer be
// dereferenced.
// Furthermore ClientUser objects are deleted in UserModel::removeUser which
// calls this very function before doing so. Thus the object shouldn't be
// deleted before this function returns anyways.
ao->removeUser(p);
}
}
}
void ClientUser::remove(ClientUser *p) {
remove(p->uiSession);
}
QString ClientUser::getFlagsString() const {
QStringList flags;
if (!qsFriendName.isEmpty())
flags << ClientUser::tr("Friend");
if (iId >= 0)
flags << ClientUser::tr("Authenticated");
if (bPrioritySpeaker)
flags << ClientUser::tr("Priority speaker");
if (bRecording)
flags << ClientUser::tr("Recording");
if (bMute)
flags << ClientUser::tr("Muted (server)");
if (bDeaf)
flags << ClientUser::tr("Deafened (server)");
if (bLocalIgnore)
flags << ClientUser::tr("Local Ignore (Text messages)");
if (bLocalIgnoreTTS)
flags << ClientUser::tr("Local Ignore (Text-To-Speech)");
if (bLocalMute)
flags << ClientUser::tr("Local Mute");
if (bSelfMute)
flags << ClientUser::tr("Muted (self)");
if (bSelfDeaf)
flags << ClientUser::tr("Deafened (self)");
return flags.join(QLatin1String(", "));
}
void ClientUser::setTalking(Settings::TalkState ts) {
if (tsState == ts)
return;
bool nstate = false;
if (ts == Settings::Passive)
nstate = true;
else if (tsState == Settings::Passive)
nstate = true;
tsState = ts;
tLastTalkStateChange.restart();
emit talkingStateChanged();
if (nstate && cChannel) {
QWriteLocker lock(&c_qrwlTalking);
if (ts == Settings::Passive)
c_qlTalking.removeAll(this);
else
c_qlTalking << this;
}
}
void ClientUser::setMute(bool mute) {
if (bMute == mute)
return;
bMute = mute;
if (!bMute)
bDeaf = false;
emit muteDeafStateChanged();
}
void ClientUser::setSuppress(bool suppress) {
if (bSuppress == suppress)
return;
bSuppress = suppress;
emit muteDeafStateChanged();
}
void ClientUser::setLocalIgnore(bool ignore) {
if (bLocalIgnore == ignore)
return;
bLocalIgnore = ignore;
emit muteDeafStateChanged();
}
void ClientUser::setLocalIgnoreTTS(bool ignoreTTS) {
bLocalIgnoreTTS = ignoreTTS;
}
void ClientUser::setLocalMute(bool mute) {
if (bLocalMute == mute)
return;
bLocalMute = mute;
emit muteDeafStateChanged();
}
void ClientUser::setDeaf(bool deaf) {
bDeaf = deaf;
if (bDeaf)
bMute = true;
emit muteDeafStateChanged();
}
void ClientUser::setSelfMute(bool mute) {
bSelfMute = mute;
if (!mute)
bSelfDeaf = false;
emit muteDeafStateChanged();
}
void ClientUser::setSelfDeaf(bool deaf) {
bSelfDeaf = deaf;
if (deaf)
bSelfMute = true;
emit muteDeafStateChanged();
}
void ClientUser::setPrioritySpeaker(bool priority) {
if (bPrioritySpeaker == priority)
return;
bPrioritySpeaker = priority;
emit prioritySpeakerStateChanged();
}
void ClientUser::setRecording(bool recording) {
if (bRecording == recording)
return;
bRecording = recording;
emit recordingStateChanged();
}
void ClientUser::setLocalVolumeAdjustment(float adjustment) {
float oldAdjustment = m_localVolume;
m_localVolume = adjustment;
emit localVolumeAdjustmentsChanged(m_localVolume, oldAdjustment);
}
void ClientUser::setLocalNickname(const QString &nickname) {
if (m_localNickname != nickname) {
m_localNickname = nickname;
emit localNicknameChanged();
}
}
bool ClientUser::lessThanOverlay(const ClientUser *first, const ClientUser *second) {
if (Global::get().s.os.osSort == OverlaySettings::LastStateChange) {
// Talkers above non-talkers
if (first->tsState != Settings::Passive && second->tsState == Settings::Passive)
return true;
if (first->tsState == Settings::Passive && second->tsState != Settings::Passive)
return false;
// Valid time above invalid time (possible when there wasn't a state-change yet)
if (first->tLastTalkStateChange.isStarted() && !second->tLastTalkStateChange.isStarted())
return true;
if (!first->tLastTalkStateChange.isStarted() && second->tLastTalkStateChange.isStarted())
return false;
// If both have a valid time
if (first->tLastTalkStateChange.isStarted() && second->tLastTalkStateChange.isStarted()) {
// Among talkers, long > short
// (if two clients are talking, the client that started first is above the other)
if (first->tsState != Settings::Passive && second->tsState != Settings::Passive)
return first->tLastTalkStateChange > second->tLastTalkStateChange;
// Among non-talkers, short -> long
// (if two clients are passive, the client that most recently stopped talking is above)
if (first->tsState == Settings::Passive && second->tsState == Settings::Passive)
return first->tLastTalkStateChange < second->tLastTalkStateChange;
}
// If both times are invalid, fall back to alphabetically (continuing below)
}
if (first->cChannel == second->cChannel || !first->cChannel || !second->cChannel)
return lessThan(first, second);
// When sorting for the overlay always place the local users
// channel above the others
ClientUser *self = c_qmUsers.value(Global::get().uiSession);
if (self) {
if (self->cChannel == first->cChannel)
return true;
else if (self->cChannel == second->cChannel)
return false;
}
return Channel::lessThan(first->cChannel, second->cChannel);
}
void ClientUser::sortUsersOverlay(QList< ClientUser * > &list) {
QReadLocker lock(&c_qrwlUsers);
std::sort(list.begin(), list.end(), ClientUser::lessThanOverlay);
}
bool ClientUser::isActive() {
if (tsState != Settings::Passive)
return true;
if (!tLastTalkStateChange.isStarted())
return false;
return tLastTalkStateChange.elapsed() < static_cast< unsigned int >(Global::get().s.os.uiActiveTime) * 1000000U;
}
/* From Channel.h
*/
void Channel::addClientUser(ClientUser *p) {
addUser(p);
p->setParent(this);
}