mirror of
https://github.com/morgan9e/gnome-shell-extension-freon
synced 2026-04-14 00:14:14 +09:00
Add CPU util
This commit is contained in:
134
freon@UshakovVasilii_Github.yahoo.com/cpuUsageUtil.js
Normal file
134
freon@UshakovVasilii_Github.yahoo.com/cpuUsageUtil.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import GLib from 'gi://GLib';
|
||||
|
||||
export default class CpuUsageUtil {
|
||||
|
||||
constructor(intervalSeconds = 2) {
|
||||
this._updated = false;
|
||||
this._readings = [];
|
||||
this._prevSnapshot = null;
|
||||
this._intervalUsec = 0;
|
||||
this._lastUpdateUsec = 0;
|
||||
this.interval = intervalSeconds;
|
||||
|
||||
// Take initial snapshot so the first real execute() can compute a delta
|
||||
this._prevSnapshot = this._readProcStat();
|
||||
}
|
||||
|
||||
get available() {
|
||||
return this._prevSnapshot !== null;
|
||||
}
|
||||
|
||||
get updated() {
|
||||
return this._updated;
|
||||
}
|
||||
|
||||
set updated(updated) {
|
||||
this._updated = updated;
|
||||
}
|
||||
|
||||
execute(callback) {
|
||||
const nowUsec = GLib.get_monotonic_time();
|
||||
const remaining = this._intervalUsec - (nowUsec - this._lastUpdateUsec);
|
||||
if (this._intervalUsec > 0 && this._lastUpdateUsec !== 0 && remaining > 0) {
|
||||
if (callback)
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updated = false;
|
||||
|
||||
try {
|
||||
const snapshot = this._readProcStat();
|
||||
if (!snapshot || !this._prevSnapshot) {
|
||||
this._prevSnapshot = snapshot;
|
||||
return;
|
||||
}
|
||||
|
||||
this._readings = [];
|
||||
|
||||
for (const [cpu, fields] of snapshot) {
|
||||
const prev = this._prevSnapshot.get(cpu);
|
||||
if (!prev)
|
||||
continue;
|
||||
|
||||
const idle = (fields.idle + fields.iowait) - (prev.idle + prev.iowait);
|
||||
const total = fields.total - prev.total;
|
||||
|
||||
if (total <= 0)
|
||||
continue;
|
||||
|
||||
const usage = 100.0 * (1.0 - idle / total);
|
||||
|
||||
this._readings.push({
|
||||
label: cpu === 'cpu' ? 'CPU Total' : cpu.toUpperCase().replace('CPU', 'CPU '),
|
||||
usage: Math.max(0, Math.min(100, usage)),
|
||||
});
|
||||
}
|
||||
|
||||
this._prevSnapshot = snapshot;
|
||||
} catch (e) {
|
||||
logError(e, '[FREON] Failed to read CPU usage');
|
||||
this._readings = [];
|
||||
} finally {
|
||||
this._lastUpdateUsec = nowUsec;
|
||||
this._updated = true;
|
||||
if (callback)
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
get usage() {
|
||||
return this._readings;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._readings = [];
|
||||
this._prevSnapshot = null;
|
||||
this._lastUpdateUsec = 0;
|
||||
}
|
||||
|
||||
set interval(seconds) {
|
||||
const clamped = Math.max(1, seconds | 0);
|
||||
this._intervalUsec = clamped * 1000000;
|
||||
this._lastUpdateUsec = 0;
|
||||
}
|
||||
|
||||
_readProcStat() {
|
||||
let ok, contents;
|
||||
try {
|
||||
[ok, contents] = GLib.file_get_contents('/proc/stat');
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
return null;
|
||||
|
||||
const text = new TextDecoder().decode(contents);
|
||||
const result = new Map();
|
||||
|
||||
for (const line of text.split('\n')) {
|
||||
if (!line.startsWith('cpu'))
|
||||
continue;
|
||||
|
||||
const parts = line.trim().split(/\s+/);
|
||||
const name = parts[0];
|
||||
|
||||
// user nice system idle iowait irq softirq steal
|
||||
const user = parseInt(parts[1]) || 0;
|
||||
const nice = parseInt(parts[2]) || 0;
|
||||
const system = parseInt(parts[3]) || 0;
|
||||
const idle = parseInt(parts[4]) || 0;
|
||||
const iowait = parseInt(parts[5]) || 0;
|
||||
const irq = parseInt(parts[6]) || 0;
|
||||
const softirq = parseInt(parts[7]) || 0;
|
||||
const steal = parseInt(parts[8]) || 0;
|
||||
|
||||
const total = user + nice + system + idle + iowait + irq + softirq + steal;
|
||||
|
||||
result.set(name, { idle, iowait, total });
|
||||
}
|
||||
|
||||
return result.size > 0 ? result : null;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import SmartctlUtil from './smartctlUtil.js';
|
||||
import NvmecliUtil from './nvmecliUtil.js';
|
||||
import BatteryUtil from './batteryUtil.js';
|
||||
import WattdUtil from './wattdUtil.js';
|
||||
import CpuUsageUtil from './cpuUsageUtil.js';
|
||||
|
||||
import FreonItem from './freonItem.js';
|
||||
|
||||
@@ -80,6 +81,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
this._initLiquidctlUtility();
|
||||
this._initBatteryUtility();
|
||||
this._initWattdUtility();
|
||||
this._initCpuUsageUtility();
|
||||
|
||||
this._initNvidiaUtility();
|
||||
this._initBumblebeeNvidiaUtility();
|
||||
@@ -104,6 +106,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
'fan' : Gio.icon_new_for_string(path + '/icons/freon-fan-symbolic.svg'),
|
||||
'power' : voltageIcon,
|
||||
'battery' : batteryIcon,
|
||||
'cpu-usage' : Gio.icon_new_for_string('utilities-system-monitor-symbolic'),
|
||||
}
|
||||
|
||||
this._menuLayout = new St.BoxLayout();
|
||||
@@ -145,6 +148,8 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
this._addSettingChangedSignal('use-generic-lmsensors', this._sensorsUtilityChanged.bind(this));
|
||||
this._addSettingChangedSignal('freeimpi-selected', this._freeipmiUtilityChanged.bind(this));
|
||||
this._addSettingChangedSignal('use-generic-liquidctl', this._liquidctlUtilityChanged.bind(this));
|
||||
this._addSettingChangedSignal('use-cpu-usage', this._cpuUsageUtilityChanged.bind(this));
|
||||
this._addSettingChangedSignal('cpu-usage-update-time', this._cpuUsageUpdateTimeChanged.bind(this));
|
||||
this._addSettingChangedSignal('use-generic-wattd', this._wattdUtilityChanged.bind(this));
|
||||
this._addSettingChangedSignal('show-battery-stats', this._batteryUtilityChanged.bind(this));
|
||||
|
||||
@@ -161,6 +166,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
this._addSettingChangedSignal('show-rotationrate', this._rerender.bind(this));
|
||||
this._addSettingChangedSignal('show-voltage', this._rerender.bind(this));
|
||||
this._addSettingChangedSignal('show-power', this._rerender.bind(this));
|
||||
this._addSettingChangedSignal('show-cpu-usage', this._rerender.bind(this));
|
||||
|
||||
|
||||
this._addSettingChangedSignal('group-temperature', this._rerender.bind(this))
|
||||
@@ -392,6 +398,31 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
this._querySensors();
|
||||
}
|
||||
|
||||
_initCpuUsageUtility() {
|
||||
if (this._settings.get_boolean('use-cpu-usage'))
|
||||
this._utils.cpuUsage = new CpuUsageUtil(this._settings.get_int('cpu-usage-update-time'));
|
||||
}
|
||||
|
||||
_destroyCpuUsageUtility() {
|
||||
if (this._utils.cpuUsage) {
|
||||
this._utils.cpuUsage.destroy();
|
||||
delete this._utils.cpuUsage;
|
||||
}
|
||||
}
|
||||
|
||||
_cpuUsageUtilityChanged() {
|
||||
this._destroyCpuUsageUtility();
|
||||
this._initCpuUsageUtility();
|
||||
this._querySensors();
|
||||
this._updateUI(true);
|
||||
}
|
||||
|
||||
_cpuUsageUpdateTimeChanged() {
|
||||
if (this._utils.cpuUsage)
|
||||
this._utils.cpuUsage.interval = this._settings.get_int('cpu-usage-update-time');
|
||||
this._querySensors();
|
||||
}
|
||||
|
||||
_initNvidiaUtility() {
|
||||
if (this._settings.get_boolean('use-gpu-nvidia'))
|
||||
this._utils.nvidia = new NvidiaUtil();
|
||||
@@ -560,6 +591,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
|
||||
this._destroyBatteryUtility();
|
||||
this._destroyWattdUtility();
|
||||
this._destroyCpuUsageUtility();
|
||||
|
||||
GLib.Source.remove(this._timeoutId);
|
||||
GLib.Source.remove(this._updateUITimeoutId);
|
||||
@@ -628,6 +660,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
let fanInfo = [];
|
||||
let voltageInfo = [];
|
||||
let powerInfo = [];
|
||||
let cpuUsageInfo = [];
|
||||
|
||||
if (this._utils.sensors && this._utils.sensors.available) {
|
||||
if (this._settings.get_boolean('show-temperature')) {
|
||||
@@ -674,6 +707,10 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
if (this._settings.get_boolean('show-power'))
|
||||
powerInfo = powerInfo.concat(this._utils.wattd.power);
|
||||
|
||||
if (this._utils.cpuUsage && this._utils.cpuUsage.available)
|
||||
if (this._settings.get_boolean('show-cpu-usage'))
|
||||
cpuUsageInfo = cpuUsageInfo.concat(this._utils.cpuUsage.usage);
|
||||
|
||||
if (this._utils.nvidia && this._utils.nvidia.available)
|
||||
if (this._settings.get_boolean('show-temperature'))
|
||||
gpuTempInfo = gpuTempInfo.concat(this._utils.nvidia.temp);
|
||||
@@ -708,12 +745,14 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
fanInfo.sort(comparator);
|
||||
voltageInfo.sort(comparator);
|
||||
powerInfo.sort(comparator);
|
||||
cpuUsageInfo.sort(comparator);
|
||||
|
||||
let tempInfo = gpuTempInfo.concat(sensorsTempInfo).concat(driveTempInfo);
|
||||
|
||||
if (tempInfo.length == 0
|
||||
&& fanInfo.length == 0
|
||||
&& voltageInfo.length == 0) {
|
||||
&& voltageInfo.length == 0
|
||||
&& cpuUsageInfo.length == 0) {
|
||||
this._sensorMenuItems = {};
|
||||
this.menu.removeAll();
|
||||
|
||||
@@ -848,6 +887,18 @@ class FreonMenuButton extends PanelMenu.Button {
|
||||
});
|
||||
}
|
||||
|
||||
if (cpuUsageInfo.length > 0 && (fanInfo.length > 0 || voltageInfo.length > 0 || powerInfo.length > 0))
|
||||
sensors.push({type : 'separator'});
|
||||
|
||||
for (let cpu of cpuUsageInfo){
|
||||
sensors.push({
|
||||
icon: 'cpu-usage',
|
||||
type: 'cpu-usage',
|
||||
label: cpu.label,
|
||||
value: _("%.1f%%").format(cpu.usage)
|
||||
});
|
||||
}
|
||||
|
||||
this._fixNames(sensors);
|
||||
|
||||
for (let k in this._hotLabels)
|
||||
|
||||
@@ -110,6 +110,26 @@ export default class FreonPreferences extends ExtensionPreferences {
|
||||
width_request: 320,
|
||||
});
|
||||
|
||||
const cpuUsageSwitch = this._addSwitch("CPU Usage", "use-cpu-usage", "Read CPU usage from /proc/stat");
|
||||
group.add(cpuUsageSwitch);
|
||||
|
||||
const cpuUsageInterval = new Adw.SpinRow({
|
||||
title: _('CPU Usage Polling Interval'),
|
||||
subtitle: _('Seconds between CPU usage updates'),
|
||||
adjustment: new Gtk.Adjustment({
|
||||
lower: 1,
|
||||
upper: 60,
|
||||
value: this._settings.get_int('cpu-usage-update-time'),
|
||||
step_increment: 1,
|
||||
}),
|
||||
});
|
||||
this._settings.bind('cpu-usage-update-time', cpuUsageInterval, 'value', Gio.SettingsBindFlags.DEFAULT);
|
||||
cpuUsageInterval.sensitive = this._settings.get_boolean('use-cpu-usage');
|
||||
this._settings.connect('changed::use-cpu-usage', () => {
|
||||
cpuUsageInterval.sensitive = this._settings.get_boolean('use-cpu-usage');
|
||||
});
|
||||
group.add(cpuUsageInterval);
|
||||
|
||||
group.add(this._addSwitch("lm-sensors", "use-generic-lmsensors", "Read sensors from lm-sensors"));
|
||||
group.add(this._addSwitch("liquidctl", "use-generic-liquidctl", "Read sensors from liquidctl (v1.7.0+)"));
|
||||
const wattdSwitch = this._addSwitch("wattd", "use-generic-wattd", "Read power data from wattd");
|
||||
@@ -180,6 +200,7 @@ export default class FreonPreferences extends ExtensionPreferences {
|
||||
group.add(this._addSwitch("Voltage", "show-voltage"));
|
||||
group.add(this._addSwitch("Power", "show-power"));
|
||||
group.add(this._addSwitch("Battery", "show-battery-stats"));
|
||||
group.add(this._addSwitch("CPU Usage", "show-cpu-usage"));
|
||||
return group
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,24 @@
|
||||
<description>Read sensors from (requires liquidctl v1.7.0 or later)</description>
|
||||
</key>
|
||||
|
||||
<key type="b" name="use-cpu-usage">
|
||||
<default>true</default>
|
||||
<summary>Enable CPU usage sensor</summary>
|
||||
<description>Read CPU usage from /proc/stat</description>
|
||||
</key>
|
||||
|
||||
<key type="b" name="show-cpu-usage">
|
||||
<default>true</default>
|
||||
<summary>Show CPU usage</summary>
|
||||
<description>Display CPU usage percentage</description>
|
||||
</key>
|
||||
|
||||
<key type="i" name="cpu-usage-update-time">
|
||||
<default>2</default>
|
||||
<summary>Seconds between CPU usage updates</summary>
|
||||
<description>Polling interval for CPU usage sensor</description>
|
||||
</key>
|
||||
|
||||
<key type="b" name="use-generic-wattd">
|
||||
<default>false</default>
|
||||
<summary>Read power data from wattd</summary>
|
||||
|
||||
Reference in New Issue
Block a user