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 NvmecliUtil from './nvmecliUtil.js';
|
||||||
import BatteryUtil from './batteryUtil.js';
|
import BatteryUtil from './batteryUtil.js';
|
||||||
import WattdUtil from './wattdUtil.js';
|
import WattdUtil from './wattdUtil.js';
|
||||||
|
import CpuUsageUtil from './cpuUsageUtil.js';
|
||||||
|
|
||||||
import FreonItem from './freonItem.js';
|
import FreonItem from './freonItem.js';
|
||||||
|
|
||||||
@@ -80,6 +81,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
this._initLiquidctlUtility();
|
this._initLiquidctlUtility();
|
||||||
this._initBatteryUtility();
|
this._initBatteryUtility();
|
||||||
this._initWattdUtility();
|
this._initWattdUtility();
|
||||||
|
this._initCpuUsageUtility();
|
||||||
|
|
||||||
this._initNvidiaUtility();
|
this._initNvidiaUtility();
|
||||||
this._initBumblebeeNvidiaUtility();
|
this._initBumblebeeNvidiaUtility();
|
||||||
@@ -104,6 +106,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
'fan' : Gio.icon_new_for_string(path + '/icons/freon-fan-symbolic.svg'),
|
'fan' : Gio.icon_new_for_string(path + '/icons/freon-fan-symbolic.svg'),
|
||||||
'power' : voltageIcon,
|
'power' : voltageIcon,
|
||||||
'battery' : batteryIcon,
|
'battery' : batteryIcon,
|
||||||
|
'cpu-usage' : Gio.icon_new_for_string('utilities-system-monitor-symbolic'),
|
||||||
}
|
}
|
||||||
|
|
||||||
this._menuLayout = new St.BoxLayout();
|
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('use-generic-lmsensors', this._sensorsUtilityChanged.bind(this));
|
||||||
this._addSettingChangedSignal('freeimpi-selected', this._freeipmiUtilityChanged.bind(this));
|
this._addSettingChangedSignal('freeimpi-selected', this._freeipmiUtilityChanged.bind(this));
|
||||||
this._addSettingChangedSignal('use-generic-liquidctl', this._liquidctlUtilityChanged.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('use-generic-wattd', this._wattdUtilityChanged.bind(this));
|
||||||
this._addSettingChangedSignal('show-battery-stats', this._batteryUtilityChanged.bind(this));
|
this._addSettingChangedSignal('show-battery-stats', this._batteryUtilityChanged.bind(this));
|
||||||
|
|
||||||
@@ -161,7 +166,8 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
this._addSettingChangedSignal('show-rotationrate', this._rerender.bind(this));
|
this._addSettingChangedSignal('show-rotationrate', this._rerender.bind(this));
|
||||||
this._addSettingChangedSignal('show-voltage', this._rerender.bind(this));
|
this._addSettingChangedSignal('show-voltage', this._rerender.bind(this));
|
||||||
this._addSettingChangedSignal('show-power', 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))
|
this._addSettingChangedSignal('group-temperature', this._rerender.bind(this))
|
||||||
this._addSettingChangedSignal('group-rotationrate', this._rerender.bind(this))
|
this._addSettingChangedSignal('group-rotationrate', this._rerender.bind(this))
|
||||||
@@ -392,6 +398,31 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
this._querySensors();
|
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() {
|
_initNvidiaUtility() {
|
||||||
if (this._settings.get_boolean('use-gpu-nvidia'))
|
if (this._settings.get_boolean('use-gpu-nvidia'))
|
||||||
this._utils.nvidia = new NvidiaUtil();
|
this._utils.nvidia = new NvidiaUtil();
|
||||||
@@ -560,6 +591,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
|
|
||||||
this._destroyBatteryUtility();
|
this._destroyBatteryUtility();
|
||||||
this._destroyWattdUtility();
|
this._destroyWattdUtility();
|
||||||
|
this._destroyCpuUsageUtility();
|
||||||
|
|
||||||
GLib.Source.remove(this._timeoutId);
|
GLib.Source.remove(this._timeoutId);
|
||||||
GLib.Source.remove(this._updateUITimeoutId);
|
GLib.Source.remove(this._updateUITimeoutId);
|
||||||
@@ -628,6 +660,7 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
let fanInfo = [];
|
let fanInfo = [];
|
||||||
let voltageInfo = [];
|
let voltageInfo = [];
|
||||||
let powerInfo = [];
|
let powerInfo = [];
|
||||||
|
let cpuUsageInfo = [];
|
||||||
|
|
||||||
if (this._utils.sensors && this._utils.sensors.available) {
|
if (this._utils.sensors && this._utils.sensors.available) {
|
||||||
if (this._settings.get_boolean('show-temperature')) {
|
if (this._settings.get_boolean('show-temperature')) {
|
||||||
@@ -674,6 +707,10 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
if (this._settings.get_boolean('show-power'))
|
if (this._settings.get_boolean('show-power'))
|
||||||
powerInfo = powerInfo.concat(this._utils.wattd.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._utils.nvidia && this._utils.nvidia.available)
|
||||||
if (this._settings.get_boolean('show-temperature'))
|
if (this._settings.get_boolean('show-temperature'))
|
||||||
gpuTempInfo = gpuTempInfo.concat(this._utils.nvidia.temp);
|
gpuTempInfo = gpuTempInfo.concat(this._utils.nvidia.temp);
|
||||||
@@ -708,12 +745,14 @@ class FreonMenuButton extends PanelMenu.Button {
|
|||||||
fanInfo.sort(comparator);
|
fanInfo.sort(comparator);
|
||||||
voltageInfo.sort(comparator);
|
voltageInfo.sort(comparator);
|
||||||
powerInfo.sort(comparator);
|
powerInfo.sort(comparator);
|
||||||
|
cpuUsageInfo.sort(comparator);
|
||||||
|
|
||||||
let tempInfo = gpuTempInfo.concat(sensorsTempInfo).concat(driveTempInfo);
|
let tempInfo = gpuTempInfo.concat(sensorsTempInfo).concat(driveTempInfo);
|
||||||
|
|
||||||
if (tempInfo.length == 0
|
if (tempInfo.length == 0
|
||||||
&& fanInfo.length == 0
|
&& fanInfo.length == 0
|
||||||
&& voltageInfo.length == 0) {
|
&& voltageInfo.length == 0
|
||||||
|
&& cpuUsageInfo.length == 0) {
|
||||||
this._sensorMenuItems = {};
|
this._sensorMenuItems = {};
|
||||||
this.menu.removeAll();
|
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);
|
this._fixNames(sensors);
|
||||||
|
|
||||||
for (let k in this._hotLabels)
|
for (let k in this._hotLabels)
|
||||||
|
|||||||
@@ -110,6 +110,26 @@ export default class FreonPreferences extends ExtensionPreferences {
|
|||||||
width_request: 320,
|
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("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+)"));
|
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");
|
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("Voltage", "show-voltage"));
|
||||||
group.add(this._addSwitch("Power", "show-power"));
|
group.add(this._addSwitch("Power", "show-power"));
|
||||||
group.add(this._addSwitch("Battery", "show-battery-stats"));
|
group.add(this._addSwitch("Battery", "show-battery-stats"));
|
||||||
|
group.add(this._addSwitch("CPU Usage", "show-cpu-usage"));
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,24 @@
|
|||||||
<description>Read sensors from (requires liquidctl v1.7.0 or later)</description>
|
<description>Read sensors from (requires liquidctl v1.7.0 or later)</description>
|
||||||
</key>
|
</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">
|
<key type="b" name="use-generic-wattd">
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
<summary>Read power data from wattd</summary>
|
<summary>Read power data from wattd</summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user