diff --git a/SysLoad.cxx b/SysLoad.cxx index 820e320..fb3fb4e 100644 --- a/SysLoad.cxx +++ b/SysLoad.cxx @@ -1,314 +1,316 @@ /* Copyright 2017,2018 Martin Koller, kollix@aon.at This file is part of liquidshell. liquidshell is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liquidshell is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with liquidshell. If not, see . */ #include #include #include #include #include #include #include #include #include //-------------------------------------------------------------------------------- const int INTERVAL_MS = 800; const int NET_INTERVAL_S = 60; // to avoid that a constant small network traffic always shows a full bar, // we set some arbitrary higher maximum value for the scale const size_t NET_INIT_SCALE = 50 * 1024; //-------------------------------------------------------------------------------- SysLoad::SysLoad(QWidget *parent) : QFrame(parent) { maxScale = NET_INIT_SCALE; setFrameShape(QFrame::StyledPanel); timeoutTimer.setInterval(INTERVAL_MS); timeoutTimer.start(); connect(&timeoutTimer, &QTimer::timeout, this, &SysLoad::fetch); connect(NetworkManager::notifier(), &NetworkManager::Notifier::primaryConnectionChanged, [this]() { maxScale = NET_INIT_SCALE; maxBytes = 0; }); // reset since we're changing network (which might be slower) // gradually decrease max network throughput to really be able to see when network traffic occurs netLoadTimer.setInterval(NET_INTERVAL_S * 1000); netLoadTimer.start(); connect(&netLoadTimer, &QTimer::timeout, this, [this]() { maxBytes = 0; if ( maxScale > NET_INIT_SCALE ) maxScale /= 2; }); setFixedWidth(60); } //-------------------------------------------------------------------------------- void SysLoad::fetch() { QFile f("/proc/stat"); if ( !f.open(QIODevice::ReadOnly) ) return; int num = 0; bool first = cpus.isEmpty(); while ( true ) { QByteArray line = f.readLine(); if ( line.isEmpty() ) break; if ( line.startsWith("cpu") && !line.startsWith("cpu ") ) { // time is in 1/100s units https://www.kernel.org/doc/Documentation/filesystems/proc.txt CpuData data; sscanf(line.constData(), "%*s %d %d %d", &data.userCPU, &data.niceCPU, &data.systemCPU); if ( first ) cpus.append(data); else { cpus[num].userPercent = (data.userCPU - cpus[num].userCPU) * 1000.0 / INTERVAL_MS; cpus[num].nicePercent = (data.niceCPU - cpus[num].niceCPU) * 1000.0 / INTERVAL_MS; cpus[num].systemPercent = (data.systemCPU - cpus[num].systemCPU) * 1000.0 / INTERVAL_MS; cpus[num].userCPU = data.userCPU; cpus[num].niceCPU = data.niceCPU; cpus[num].systemCPU = data.systemCPU; num++; } } } f.close(); // get memory information - size_t memTotal = 0, memFree = 0, swapTotal = 0, swapFree = 0, cached = 0; + size_t memTotal = 0, memFree = 0, swapTotal = 0, swapFree = 0, cached = 0, buffers = 0; f.setFileName("/proc/meminfo"); if ( f.open(QIODevice::ReadOnly) ) { while ( true ) { QByteArray line = f.readLine(); if ( line.isEmpty() ) break; if ( line.startsWith("MemTotal:") ) sscanf(line, "%*s %zd kB", &memTotal); else if ( line.startsWith("MemFree:") ) sscanf(line, "%*s %zd kB", &memFree); else if ( line.startsWith("SwapTotal:") ) sscanf(line, "%*s %zd kB", &swapTotal); else if ( line.startsWith("SwapFree:") ) sscanf(line, "%*s %zd kB", &swapFree); else if ( line.startsWith("Cached:") ) sscanf(line, "%*s %zd kB", &cached); + else if ( line.startsWith("Buffers:") ) sscanf(line, "%*s %zd kB", &buffers); } + cached += buffers; memData.memPercent = memTotal ? (double(memTotal - memFree) / double(memTotal)) * 100.0 : 0.0; memData.memCachedPercent = memTotal ? (double(cached) / double(memTotal)) * 100.0 : 0.0; memData.swapPercent = swapTotal ? (double(swapTotal - swapFree) / double(swapTotal)) * 100.0 : 0.0; f.close(); } f.setFileName("/proc/cpuinfo"); // get speed of cores if ( f.open(QIODevice::ReadOnly) ) { int num = 0; while ( true ) { QByteArray line = f.readLine(); if ( line.isEmpty() ) break; if ( line.startsWith("cpu MHz") ) { if ( num < cpus.count() ) sscanf(line, "cpu MHz %*s %lf", &cpus[num++].MHz); } } f.close(); } f.setFileName("/proc/net/dev"); sumSent = sumReceived = 0; if ( f.open(QIODevice::ReadOnly) ) { while ( true ) { QByteArray line = f.readLine(); if ( line.isEmpty() ) break; int colon = line.indexOf(':'); if ( colon > 1 ) { QByteArray device = line.left(colon).trimmed(); size_t received = 0, sent = 0; sscanf(line.data() + colon + 1, "%zd %*d %*d %*d %*d %*d %*d %*d %zd", &received, &sent); if ( !netDevs.contains(device) ) { NetworkData data; data.prevReceived = received; data.prevSent = sent; data.valid = false; // first scan not valid since we count differences netDevs.insert(device, data); } else { NetworkData &data = netDevs[device]; data.received = received - data.prevReceived; data.sent = sent - data.prevSent; data.prevReceived = received; data.prevSent = sent; data.valid = true; if ( (device != "lo") && // don't count loopback adapter !device.startsWith("tun") ) // TODO: correct ? { sumReceived += data.received; sumSent += data.sent; } } } } f.close(); } maxBytes = std::max(maxBytes, (sumReceived + sumSent)); maxScale = std::max(maxBytes, maxScale); update(); QString tip; for (int i = 0; i < cpus.count(); i++) { if ( i ) tip += "
"; tip += i18n("CPU %1: %2% (%3 MHz)", i, locale().toString(std::min(100.0, cpus[i].userPercent + cpus[i].nicePercent + cpus[i].systemPercent), 'f', 1), static_cast(cpus[i].MHz)); } tip += "
"; memFree += cached; // show also the cached memory as free (for user applications) size_t memUsed = memTotal - memFree; size_t swapUsed = swapTotal - swapFree; int memUsedPercent = memTotal ? memUsed * 100 / memTotal : 0; int swapUsedPercent = swapTotal ? swapUsed * 100 / swapTotal : 0; tip += i18n("Memory Total: %1 MB (%2 GB)" , memTotal / 1024, locale().toString(memTotal / 1024.0 / 1024.0, 'f', 2)); tip += "
"; tip += i18n("Memory Used: %1 MB (%2 GB) %3%", memUsed / 1024, locale().toString(memUsed / 1024.0 / 1024.0, 'f', 2), memUsedPercent); tip += "
"; tip += i18n("Memory Free: %1 MB (%2 GB)" , memFree / 1024, locale().toString(memFree / 1024.0 / 1024.0, 'f', 2)); tip += "
"; tip += i18n("Swap Total: %1 MB (%2 GB)" , swapTotal / 1024, locale().toString(swapTotal / 1024.0 / 1024.0, 'f', 2)); tip += "
"; tip += i18n("Swap Used: %1 MB (%2 GB) %3%" , swapUsed / 1024, locale().toString(swapUsed / 1024.0 / 1024.0, 'f', 2), swapUsedPercent); tip += "
"; tip += i18n("Swap Free: %1 MB (%2 GB)" , swapFree / 1024, locale().toString(swapFree / 1024.0 / 1024.0, 'f', 2)); tip += "
"; tip += i18n("Net send/receive: %1/%2 KB/sec", locale().toString((sumSent / 1024.0) / (INTERVAL_MS / 1000.0), 'f', 2), locale().toString((sumReceived / 1024.0) / (INTERVAL_MS / 1000.0), 'f', 2)); tip += "
"; tip += i18n("Net max (last %2 secs): %1 KB/sec", locale().toString((maxBytes / 1024.0) / (INTERVAL_MS / 1000.0), 'f', 2), NET_INTERVAL_S); if ( underMouse() ) QToolTip::showText(QCursor::pos(), QLatin1String("") + tip + QLatin1String(""), this, rect()); } //-------------------------------------------------------------------------------- void SysLoad::paintEvent(QPaintEvent *event) { QFrame::paintEvent(event); const int cpuBars = cpuSummaryBar ? 1 : cpus.count(); int const barWidth = contentsRect().width() / (cpuBars + 2 + 1); // mem usage 2 bars + netSum int x = contentsRect().x(), y = contentsRect().y() + contentsRect().height(); QPainter painter(this); // cpu QVector drawCpus; if ( !cpuSummaryBar ) drawCpus = cpus; else { CpuData sum; for (const CpuData &data : cpus) { sum.userPercent += data.userPercent; sum.systemPercent += data.systemPercent; sum.nicePercent += data.nicePercent; } sum.userPercent /= cpus.count(); sum.systemPercent /= cpus.count(); sum.nicePercent /= cpus.count(); drawCpus.append(sum); } for (const CpuData &data : drawCpus) { int h = contentsRect().height() * (data.userPercent / 100.0); painter.fillRect(x, y - h, barWidth, h, cpuUserColor); y -= h; h = contentsRect().height() * (data.systemPercent / 100.0); painter.fillRect(x, y - h, barWidth, h, cpuSystemColor); y -= h; h = contentsRect().height() * (data.nicePercent / 100.0); painter.fillRect(x, y - h, barWidth, h, cpuNiceColor); x += barWidth; y = contentsRect().y() + contentsRect().height(); } // memory int h = contentsRect().height() * (memData.memPercent / 100.0); painter.fillRect(x, y - h, barWidth, h, memUsedColor); y -= h; h = contentsRect().height() * (memData.memCachedPercent / 100.0); painter.fillRect(x, y, barWidth, h, memCachedColor); x += barWidth; y = contentsRect().y() + contentsRect().height(); h = contentsRect().height() * (memData.swapPercent / 100.0); painter.fillRect(x, y - h, barWidth, h, memSwapColor); // net x += barWidth; y = contentsRect().y() + contentsRect().height(); h = contentsRect().height() * (double(sumReceived) / maxScale); painter.fillRect(x, y - h, barWidth, h, netReceivedColor); y -= h; h = contentsRect().height() * (double(sumSent) / maxScale); painter.fillRect(x, y - h, barWidth, h, netSentColor); } //-------------------------------------------------------------------------------- void SysLoad::mousePressEvent(QMouseEvent *event) { if ( event->button() == Qt::LeftButton ) { KRun::runCommand("ksysguard", this); } } //--------------------------------------------------------------------------------