// Copyright 2005-2019 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 . #include "ClientUser.h" #include "Channel.h" #include "Global.h" #include "AudioOutput.h" QHash ClientUser::c_qmUsers; QReadWriteLock ClientUser::c_qrwlUsers; QList ClientUser::c_qlTalking; QReadWriteLock ClientUser::c_qrwlTalking; ClientUser::ClientUser(QObject *p) : QObject(p), tsState(Settings::Passive), tLastTalkStateChange(false), bLocalIgnore(false), bLocalMute(false), fPowerMin(0.0f), fPowerMax(0.0f), fAverageAvailable(0.0f), fLocalVolume(1.0f), iFrames(0), iSequence(0) { } ClientUser *ClientUser::get(unsigned int uiSession) { QReadLocker lock(&c_qrwlUsers); ClientUser *p = c_qmUsers.value(uiSession); return p; } QList ClientUser::getTalking() { QReadLocker lock(&c_qrwlTalking); return c_qlTalking; } QList ClientUser::getActive() { QReadLocker lock(&c_qrwlUsers); QList 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; 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 NULL; } void ClientUser::remove(unsigned int uiSession) { QWriteLocker lock(&c_qrwlUsers); ClientUser *p = c_qmUsers.take(uiSession); if (p) { if (p->cChannel) p->cChannel->removeUser(p); AudioOutputPtr ao = g.ao; if (ao) ao->removeBuffer(p); if (p->tsState != Settings::Passive) { QWriteLocker writeLock(&c_qrwlTalking); c_qlTalking.removeAll(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 (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::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(); } bool ClientUser::lessThanOverlay(const ClientUser *first, const ClientUser *second) { if (g.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 == NULL || second->cChannel == NULL) return lessThan(first, second); // When sorting for the overlay always place the local users // channel above the others ClientUser *self = c_qmUsers.value(g.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 &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() < g.s.os.uiActiveTime * 1000000U; } /* From Channel.h */ void Channel::addClientUser(ClientUser *p) { addUser(p); p->setParent(this); }