Files
fedora-tools/framework-laptop-tools/src/tooltip.cpp
T

94 lines
5.3 KiB
C++

// SPDX-License-Identifier: MIT
#include "tooltip.h"
#include "readingformat.h"
#include <cmath>
#include <algorithm>
QVector<TooltipSensor> tooltipSensors(const QVector<Sensor> &sensors)
{
QVector<TooltipSensor> result{{"cpu", "CPU", {}}, {"memory", "RAM", {}}, {"nvme", "NVMe", {}},
{"battery", "Battery", {}}, {"board", "Board", {}}};
const QStringList prefixes{"cros_ec/peci-temp", "spd5118/", "nvme/Composite", "cros_ec/battery_temp@b", "cros_ec/local_f75397@4c"};
for (int i = 0; i < result.size(); ++i) {
for (const auto &sensor : sensors) if (sensor.id.startsWith(prefixes[i])) { result[i].id = sensor.id; break; }
if (i == 0 && result[i].id.isEmpty())
for (const auto &sensor : sensors) if (sensor.id.startsWith("coretemp/Package id")) { result[i].id = sensor.id; break; }
}
return result;
}
bool tooltipEnabled(const QVariantMap &settings, const QString &key, bool fallback)
{
return settings.value("tray/hover/" + key, fallback).toBool();
}
int tooltipAppCount(const QVariantMap &settings)
{
return std::clamp(settings.value("tray/hover/topApps", settings.value("tray/hover/topApp", false).toBool() ? 1 : 0).toInt(), 0, 3);
}
QString batteryTooltip(const QVariantMap &battery, int limit, const QVariantMap &upower)
{
if (battery.isEmpty()) return "Battery unavailable";
QString text = "Battery " + (battery.contains("capacity") ? QString::number(battery["capacity"].toInt()) + "%" : "—");
const auto energy = [&](const QString &key) {
const double value = battery.value(key, NAN).toDouble();
if (!std::isfinite(value) || value < 0) return QString("—");
QString number = QString::number(qRound64(value));
for (int i = number.size() - 3; i > 0; i -= 3) number.insert(i, ' ');
return number;
};
text += " · " + energy("remainingMWh") + " mWh / " + energy("fullMWh") + " mWh";
const auto watts = batteryRate(battery);
text += "\n\u2003" + (watts ? (*watts > 0 ? "+" : "") + formatReading(*watts) + " W" : "— W");
const bool charging = battery["state"] == "Charging", discharging = battery["state"] == "Discharging";
double seconds = 0;
bool approximate = false;
if (discharging) seconds = upower.value("TimeToEmpty").toDouble();
else if (charging && limit == 100) seconds = upower.value("TimeToFull").toDouble();
else if (charging && limit < 100) {
approximate = true;
if (battery["current_now"].toDouble() > 0 && battery.contains("charge_now") && battery["charge_full"].toDouble() > 0)
seconds = (battery["charge_full"].toDouble() * limit / 100 - battery["charge_now"].toDouble()) / battery["current_now"].toDouble() * 3600;
else if (watts && *watts > 0 && battery.contains("energy_now") && battery["energy_full"].toDouble() > 0)
seconds = (battery["energy_full"].toDouble() * limit / 100 - battery["energy_now"].toDouble()) / (*watts * 1e6) * 3600;
}
if (std::isfinite(seconds) && seconds > 0) {
const auto minutes = qint64(std::ceil(seconds / 60));
text += QString(" · %1%2 h %3 min to %4%").arg(approximate ? "≈" : "")
.arg(minutes / 60).arg(minutes % 60).arg(discharging ? 0 : limit);
} else if (charging && battery.contains("capacity") && battery["capacity"].toInt() >= limit)
text += QString(" · %1% limit reached").arg(limit);
else if (charging || discharging) text += " · Time remaining unavailable";
else text += " · " + battery["state"].toString();
return text;
}
QString trayTooltip(const QVariantMap &settings, const QMap<QString, double> &values,
const QVector<TooltipSensor> &temperatures, const QString &battery, const QStringList &topApps,
const QVector<Sensor> &powerSensors)
{
const auto reading = [&](const QString &id, int decimals) {
const auto it = values.constFind(id);
return it != values.cend() && std::isfinite(*it) ? QString::number(*it, 'f', decimals) : QString("—");
};
QStringList lines;
if (tooltipEnabled(settings, "cpu")) {
lines << "CPU usage: " + reading("cpu-usage", 1) + " %";
const int count = tooltipAppCount(settings);
if (count && topApps.isEmpty()) lines << "\u2003CPU app readings unavailable";
for (int i = 0; i < std::min(count, int(topApps.size())); ++i) lines << "\u2003" + topApps[i];
}
if (tooltipEnabled(settings, "battery")) lines << battery;
if (tooltipEnabled(settings, "fan")) lines << "Fan speed: " + reading("fan", 0) + " RPM (" + reading("fan-duty", 0) + " %)";
QStringList power;
for (const auto &sensor : powerSensors)
if (tooltipEnabled(settings, sensor.id, false))
power << sensor.name + ": " + formatReading(values.value(sensor.id, NAN)) + " W";
if (power.size() > 1) lines << "Power consumption:";
for (const auto &line : power) lines << (power.size() > 1 ? "\u2003" : "") + line;
QStringList temps;
for (const auto &sensor : temperatures)
if (!sensor.id.isEmpty() && tooltipEnabled(settings, "temperature/" + sensor.key))
temps << sensor.name + " " + formatReading(values.value(sensor.id, NAN)) + "°C";
if (temps.size() > 1) lines << "Temperatures:";
for (const auto &temp : temps) lines << (temps.size() > 1 ? "\u2003" : "") + temp;
return lines.join('\n');
}