From fca9e6fc035817f2f2728a9ec8f93b103f5d33ce Mon Sep 17 00:00:00 2001 From: Hartmnt Date: Mon, 6 Jan 2025 16:24:58 +0000 Subject: [PATCH] FEAT(server, client): Add rolling connection quality information --- src/CMakeLists.txt | 1 + src/Mumble.proto | 6 + src/crypto/CryptState.cpp | 51 +++ src/crypto/CryptState.h | 34 +- src/crypto/CryptStateOCB2.cpp | 1 + src/mumble/UserInformation.cpp | 68 +++- src/mumble/UserInformation.ui | 354 +++++++++++++++------ src/mumble/widgets/AccessibleQGroupBox.cpp | 18 +- src/murmur/Messages.cpp | 14 + 9 files changed, 436 insertions(+), 111 deletions(-) create mode 100644 src/crypto/CryptState.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c73ee08c3b..434a6cb94de 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,6 +85,7 @@ set(SHARED_SOURCES "crypto/CryptographicHash.cpp" "crypto/CryptographicRandom.cpp" + "crypto/CryptState.cpp" "crypto/CryptStateOCB2.cpp" "${3RDPARTY_DIR}/arc4random/arc4random_uniform.cpp" diff --git a/src/Mumble.proto b/src/Mumble.proto index 169c18a6925..bfcd4c362c7 100644 --- a/src/Mumble.proto +++ b/src/Mumble.proto @@ -553,6 +553,12 @@ message UserStats { // True if the user has a strong certificate. optional bool strong_certificate = 18 [default = false]; optional bool opus = 19 [default = false]; + + optional uint32 rolling_time = 20; + // Rolling packet statistics for packets received from the client. + optional Stats rolling_from_client = 21; + // Rolling packet statistics for packets sent by the server. + optional Stats rolling_from_server = 22; } // Used by the client to request binary data from the server. By default large diff --git a/src/crypto/CryptState.cpp b/src/crypto/CryptState.cpp new file mode 100644 index 00000000000..23b6f053462 --- /dev/null +++ b/src/crypto/CryptState.cpp @@ -0,0 +1,51 @@ +// Copyright 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 "CryptState.h" + +void CryptState::handleRollingStats() { + bool noData = m_statsLocalReference.empty() && m_statsRemoteReference.empty(); + + std::chrono::time_point< std::chrono::steady_clock > now = std::chrono::steady_clock::now(); + + // Update every 5 seconds + if ((now - m_rollingTimer) > m_rollingScanInterval && !noData) { + return; + } + + m_rollingTimer = std::chrono::steady_clock::now(); + + PacketStatsSnapshot snapshotLocal; + snapshotLocal.stats = m_statsLocal; + snapshotLocal.timestamp = now; + m_statsLocalReference.push(snapshotLocal); + + PacketStatsSnapshot snapshotRemote; + snapshotRemote.stats = m_statsRemote; + snapshotRemote.timestamp = now; + m_statsRemoteReference.push(snapshotRemote); + + while (!m_statsLocalReference.empty() && (m_statsLocalReference.front().timestamp + m_rollingWindow) < now) { + m_statsLocalReference.pop(); + } + + while (!m_statsRemoteReference.empty() && (m_statsRemoteReference.front().timestamp + m_rollingWindow) < now) { + m_statsRemoteReference.pop(); + } + + if (!m_statsLocalReference.empty()) { + m_statsLocalRolling.good = m_statsLocal.good - m_statsLocalReference.front().stats.good; + m_statsLocalRolling.late = m_statsLocal.late - m_statsLocalReference.front().stats.late; + m_statsLocalRolling.lost = m_statsLocal.lost - m_statsLocalReference.front().stats.lost; + m_statsLocalRolling.resync = m_statsLocal.resync - m_statsLocalReference.front().stats.resync; + } + + if (!m_statsRemoteReference.empty()) { + m_statsRemoteRolling.good = m_statsRemote.good - m_statsRemoteReference.front().stats.good; + m_statsRemoteRolling.late = m_statsRemote.late - m_statsRemoteReference.front().stats.late; + m_statsRemoteRolling.lost = m_statsRemote.lost - m_statsRemoteReference.front().stats.lost; + m_statsRemoteRolling.resync = m_statsRemote.resync - m_statsRemoteReference.front().stats.resync; + } +} diff --git a/src/crypto/CryptState.h b/src/crypto/CryptState.h index 6a9dc66ae7d..cdeb3f1bc67 100644 --- a/src/crypto/CryptState.h +++ b/src/crypto/CryptState.h @@ -7,21 +7,43 @@ #define MUMBLE_CRYPTSTATE_H_ #include "Timer.h" +#include +#include #include struct PacketStats { - unsigned int good = 0; - unsigned int late = 0; - unsigned int lost = 0; - unsigned int rsync = 0; + unsigned int good = 0; + unsigned int late = 0; + unsigned int lost = 0; + unsigned int resync = 0; +}; + +struct PacketStatsSnapshot { + PacketStats stats; + std::chrono::time_point< std::chrono::steady_clock > timestamp; }; class CryptState { private: Q_DISABLE_COPY(CryptState) + + const std::chrono::seconds m_rollingScanInterval = std::chrono::seconds(5); + std::chrono::time_point< std::chrono::steady_clock > m_rollingTimer = std::chrono::steady_clock::now(); + + std::queue< PacketStatsSnapshot > m_statsLocalReference; + std::queue< PacketStatsSnapshot > m_statsRemoteReference; + +protected: + void handleRollingStats(); + public: - PacketStats m_statsLocal = {}; - PacketStats m_statsRemote = {}; + PacketStats m_statsLocal = {}; + PacketStats m_statsRemote = {}; + PacketStats m_statsLocalRolling = {}; + PacketStats m_statsRemoteRolling = {}; + + /// This is the packet statistics sliding time window size in seconds + std::chrono::duration< uint32_t, std::ratio< 1 > > m_rollingWindow = std::chrono::seconds(60 * 5); Timer tLastGood; Timer tLastRequest; diff --git a/src/crypto/CryptStateOCB2.cpp b/src/crypto/CryptStateOCB2.cpp index 733ee12a4da..9b1c5d763d9 100644 --- a/src/crypto/CryptStateOCB2.cpp +++ b/src/crypto/CryptStateOCB2.cpp @@ -220,6 +220,7 @@ bool CryptStateOCB2::decrypt(const unsigned char *source, unsigned char *dst, un m_statsLocal.lost -= static_cast< unsigned int >(std::abs(lost)); } + handleRollingStats(); tLastGood.restart(); return true; } diff --git a/src/mumble/UserInformation.cpp b/src/mumble/UserInformation.cpp index a735763f319..eccc86e6cc9 100644 --- a/src/mumble/UserInformation.cpp +++ b/src/mumble/UserInformation.cpp @@ -163,8 +163,12 @@ void UserInformation::update(const MumbleProto::UserStats &msg) { qlTCPVar->setText(QString::number(msg.tcp_ping_var() > 0.0f ? sqrtf(msg.tcp_ping_var()) : 0.0f, 'f', 2)); qlUDPVar->setText(QString::number(msg.udp_ping_var() > 0.0f ? sqrtf(msg.udp_ping_var()) : 0.0f, 'f', 2)); - if (msg.has_from_client() && msg.has_from_server()) { - qgbUDP->setVisible(true); + bool hasTotalStats = msg.has_from_client() && msg.has_from_server(); + bool hasRollingStats = msg.has_rolling_time() && msg.has_rolling_from_client() && msg.has_rolling_from_server(); + + qgbUDP->setVisible(hasTotalStats || hasRollingStats); + + if (hasTotalStats) { const MumbleProto::UserStats_Stats &from = msg.from_client(); qlFromGood->setText(QString::number(from.good())); qlFromLate->setText(QString::number(from.late())); @@ -179,17 +183,65 @@ void UserInformation::update(const MumbleProto::UserStats &msg) { quint32 allFromPackets = from.good() + from.late() + from.lost(); qlFromLatePercent->setText( - QString::number(allFromPackets > 0 ? from.late() * 100.0 / allFromPackets : 0., 'f', 2)); + QString::number(allFromPackets > 0 ? from.late() * 100.0 / allFromPackets : 0., 'f', 1)); qlFromLostPercent->setText( - QString::number(allFromPackets > 0 ? from.lost() * 100.0 / allFromPackets : 0., 'f', 2)); + QString::number(allFromPackets > 0 ? from.lost() * 100.0 / allFromPackets : 0., 'f', 1)); quint32 allToPackets = to.good() + to.late() + to.lost(); - qlToLatePercent->setText(QString::number(allToPackets > 0 ? to.late() * 100.0 / allToPackets : 0., 'f', 2)); - qlToLostPercent->setText(QString::number(allToPackets > 0 ? to.lost() * 100.0 / allToPackets : 0., 'f', 2)); - } else { - qgbUDP->setVisible(false); + qlToLatePercent->setText(QString::number(allToPackets > 0 ? to.late() * 100.0 / allToPackets : 0., 'f', 1)); + qlToLostPercent->setText(QString::number(allToPackets > 0 ? to.lost() * 100.0 / allToPackets : 0., 'f', 1)); } + if (hasRollingStats) { + const MumbleProto::UserStats_Stats &from = msg.rolling_from_client(); + qlFromGoodRolling->setText(QString::number(from.good())); + qlFromLateRolling->setText(QString::number(from.late())); + qlFromLostRolling->setText(QString::number(from.lost())); + qlFromResyncRolling->setText(QString::number(from.resync())); + + const MumbleProto::UserStats_Stats &to = msg.rolling_from_server(); + qlToGoodRolling->setText(QString::number(to.good())); + qlToLateRolling->setText(QString::number(to.late())); + qlToLostRolling->setText(QString::number(to.lost())); + qlToResyncRolling->setText(QString::number(to.resync())); + + quint32 allFromPackets = from.good() + from.late() + from.lost(); + qlFromLatePercentRolling->setText( + QString::number(allFromPackets > 0 ? from.late() * 100.0 / allFromPackets : 0., 'f', 1)); + qlFromLostPercentRolling->setText( + QString::number(allFromPackets > 0 ? from.lost() * 100.0 / allFromPackets : 0., 'f', 1)); + + quint32 allToPackets = to.good() + to.late() + to.lost(); + qlToLatePercentRolling->setText( + QString::number(allToPackets > 0 ? to.late() * 100.0 / allToPackets : 0., 'f', 1)); + qlToLostPercentRolling->setText( + QString::number(allToPackets > 0 ? to.lost() * 100.0 / allToPackets : 0., 'f', 1)); + + uint32_t rollingSeconds = msg.rolling_time(); + QString rollingText = tr("Last %1 %2:"); + if (rollingSeconds < 120) { + qliRolling->setText(rollingText.arg(QString::number(rollingSeconds)).arg(tr("seconds"))); + } else { + qliRolling->setText(rollingText.arg(QString::number(rollingSeconds / 60)).arg(tr("minutes"))); + } + } + + qlFromGoodRolling->setVisible(hasRollingStats); + qlFromLateRolling->setVisible(hasRollingStats); + qlFromLostRolling->setVisible(hasRollingStats); + qlFromResyncRolling->setVisible(hasRollingStats); + qlToGoodRolling->setVisible(hasRollingStats); + qlToLateRolling->setVisible(hasRollingStats); + qlToLostRolling->setVisible(hasRollingStats); + qlToResyncRolling->setVisible(hasRollingStats); + qlFromLatePercentRolling->setVisible(hasRollingStats); + qlFromLostPercentRolling->setVisible(hasRollingStats); + qlToLatePercentRolling->setVisible(hasRollingStats); + qlToLostPercentRolling->setVisible(hasRollingStats); + qliRolling->setVisible(hasRollingStats); + qliRollingFrom->setVisible(hasRollingStats); + qliRollingTo->setVisible(hasRollingStats); + if (msg.has_onlinesecs()) { if (msg.has_idlesecs()) qlTime->setText( diff --git a/src/mumble/UserInformation.ui b/src/mumble/UserInformation.ui index 54c8374f8c3..c3a12c580e1 100644 --- a/src/mumble/UserInformation.ui +++ b/src/mumble/UserInformation.ui @@ -7,7 +7,7 @@ 0 0 488 - 658 + 713 @@ -39,10 +39,10 @@ - Qt::PlainText + Qt::TextFormat::PlainText - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -94,7 +94,7 @@ - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -110,10 +110,10 @@ - Qt::PlainText + Qt::TextFormat::PlainText - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -165,10 +165,10 @@ - Qt::PlainText + Qt::TextFormat::PlainText - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -194,7 +194,7 @@ Pings received - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -204,7 +204,7 @@ Average ping - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -214,7 +214,7 @@ Ping deviation - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -231,10 +231,10 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -244,10 +244,10 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -257,10 +257,10 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -277,10 +277,10 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -290,10 +290,10 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -303,10 +303,10 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -319,13 +319,33 @@ UDP Network statistics + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + to client rolling average + + + To Client + + + Good - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -335,145 +355,277 @@ Late - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - Lost + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - Resync + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - From Client + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - + + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse + + + + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - + + + + Lost + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - + To Client + + + + + true + + + + Last X minutes: + + + + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + - + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + % + + + % lost + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + from client rolling average + + + From Client + + + + + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - + + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + Resync + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -482,58 +634,76 @@ % + + % late + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - % + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse - - + + - + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - + From Client - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + Total: - - + + - + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -565,7 +735,7 @@ - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -588,7 +758,7 @@ - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse diff --git a/src/mumble/widgets/AccessibleQGroupBox.cpp b/src/mumble/widgets/AccessibleQGroupBox.cpp index a0971bd5d4f..fc508771861 100644 --- a/src/mumble/widgets/AccessibleQGroupBox.cpp +++ b/src/mumble/widgets/AccessibleQGroupBox.cpp @@ -27,6 +27,11 @@ QString AccessibleQGroupBox::textAtPosition(QGridLayout *gridLayout, int y, int return ""; } + QString accessibleName = label->accessibleName(); + if (!accessibleName.isEmpty()) { + return accessibleName; + } + QString content = Mumble::Accessibility::removeHTMLTags(label->text()); if (content.trimmed().isEmpty()) { content = tr("empty"); @@ -47,11 +52,14 @@ void AccessibleQGroupBox::updateAccessibleText() { for (int y = tableMode ? 1 : 0; y < gridLayout->rowCount(); y++) { for (int x = tableMode ? 1 : 0; x < gridLayout->columnCount(); x++) { if (tableMode) { - text += textAtPosition(gridLayout, y, 0); - text += " "; - text += textAtPosition(gridLayout, 0, x); - text += ", "; - text += textAtPosition(gridLayout, y, x); + QString cellContent = textAtPosition(gridLayout, y, x); + if (!cellContent.isEmpty()) { + text += textAtPosition(gridLayout, y, 0); + text += " "; + text += textAtPosition(gridLayout, 0, x); + text += ", "; + text += cellContent; + } } else { text += textAtPosition(gridLayout, y, x); } diff --git a/src/murmur/Messages.cpp b/src/murmur/Messages.cpp index 8d0dc3abd2a..b08758861ba 100644 --- a/src/murmur/Messages.cpp +++ b/src/murmur/Messages.cpp @@ -2285,6 +2285,20 @@ void Server::msgUserStats(ServerUser *uSource, MumbleProto::UserStats &msg) { mpusss->set_late(pDstServerUser->csCrypt->m_statsRemote.late); mpusss->set_lost(pDstServerUser->csCrypt->m_statsRemote.lost); mpusss->set_resync(pDstServerUser->csCrypt->m_statsRemote.resync); + + mpusss = msg.mutable_rolling_from_client(); + mpusss->set_good(pDstServerUser->csCrypt->m_statsLocalRolling.good); + mpusss->set_late(pDstServerUser->csCrypt->m_statsLocalRolling.late); + mpusss->set_lost(pDstServerUser->csCrypt->m_statsLocalRolling.lost); + mpusss->set_resync(pDstServerUser->csCrypt->m_statsLocalRolling.resync); + + mpusss = msg.mutable_rolling_from_server(); + mpusss->set_good(pDstServerUser->csCrypt->m_statsRemoteRolling.good); + mpusss->set_late(pDstServerUser->csCrypt->m_statsRemoteRolling.late); + mpusss->set_lost(pDstServerUser->csCrypt->m_statsRemoteRolling.lost); + mpusss->set_resync(pDstServerUser->csCrypt->m_statsRemoteRolling.resync); + + msg.set_rolling_time(pDstServerUser->csCrypt->m_rollingWindow.count()); } msg.set_udp_packets(pDstServerUser->uiUDPPackets);