206 lines
6.1 KiB
TypeScript
206 lines
6.1 KiB
TypeScript
import { exec, execAsync, interval, Variable } from "astal";
|
|
import { Gtk } from "astal/gtk4";
|
|
|
|
const FETCH_INTERVAL = 2000;
|
|
|
|
const cpuUtil = Variable("0%");
|
|
const ramUtil = Variable("0%");
|
|
const ramUsed = Variable("0MiB");
|
|
const gpuUtil = Variable("0%");
|
|
let gpuName = "card1";
|
|
let enabled = false;
|
|
|
|
const refreshStats = (): Stats => {
|
|
gpuName = exec(`/bin/bash -c "ls /sys/class/drm/ | grep '^card[0-9]*$'"`);
|
|
const cpuNameInSensors = "CPUTIN";
|
|
const stats = {
|
|
kernel: exec("uname -sr"),
|
|
cpuTemp: exec(
|
|
`/bin/bash -c "sensors | grep -m1 ${cpuNameInSensors} | awk '{print $2}'"`,
|
|
),
|
|
cpuClk: exec(
|
|
`awk '/cpu MHz/ {sum+=$4; ++n} END {print sum/n " MHz"}' /proc/cpuinfo`,
|
|
),
|
|
gpuTemp: exec(
|
|
`/bin/bash -c "sensors | grep -E 'edge' | awk '{print $2}'"`,
|
|
),
|
|
gpuClk: exec(
|
|
`/bin/bash -c "cat /sys/class/drm/${gpuName}/device/pp_dpm_sclk | grep '\\*' | awk '{print $2 $3}'"`,
|
|
),
|
|
vram:
|
|
Math.round(
|
|
parseInt(
|
|
exec(
|
|
`cat /sys/class/drm/${gpuName}/device/mem_info_vram_used`,
|
|
),
|
|
) /
|
|
1024 /
|
|
1024,
|
|
) + "MiB",
|
|
availableVRAM:
|
|
Math.round(
|
|
parseInt(
|
|
exec(
|
|
`cat /sys/class/drm/${gpuName}/device/mem_info_vram_total`,
|
|
),
|
|
) /
|
|
1024 /
|
|
1024,
|
|
) + "MiB",
|
|
};
|
|
|
|
return stats;
|
|
};
|
|
|
|
const systemStats: Variable<Stats> = Variable(refreshStats());
|
|
|
|
const availableFeatures = {
|
|
cpu: true,
|
|
ram: true,
|
|
};
|
|
|
|
const featureTest = () => {
|
|
// Check if awk & sed are available
|
|
try {
|
|
exec("awk -V");
|
|
exec("sed --version");
|
|
enabled = true;
|
|
} catch (e) {
|
|
printerr(
|
|
"[ SysInfo ] AWK or SED missing! No system info will be available",
|
|
);
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
// Check if mpstat is available
|
|
try {
|
|
exec("mpstat -V");
|
|
} catch (e) {
|
|
availableFeatures.cpu = false;
|
|
printerr(
|
|
"[ SysInfo ] Feature Test for CPU info failed. mpstat from the sysstat package missing!",
|
|
);
|
|
}
|
|
};
|
|
|
|
const info = () => {
|
|
return (
|
|
<box vertical>
|
|
<label
|
|
label={"System Information"}
|
|
cssClasses={["title-2"]}
|
|
></label>
|
|
<Gtk.Separator marginTop={5} marginBottom={10}></Gtk.Separator>
|
|
<label
|
|
vexpand
|
|
halign={Gtk.Align.START}
|
|
hexpand
|
|
label={ramUsed(used => {
|
|
return "RAM: " + used + ` (${ramUtil.get()}%)`;
|
|
})}
|
|
></label>
|
|
<label
|
|
label={systemStats(stats => {
|
|
return `CPU: ${stats.cpuTemp}, ${stats.cpuClk}
|
|
GPU: ${stats.gpuTemp}, ${stats.gpuClk} (${stats.vram} / ${stats.availableVRAM})
|
|
Kernel: ${stats.kernel}`;
|
|
})}
|
|
></label>
|
|
<Gtk.Separator marginTop={10}></Gtk.Separator>
|
|
<button
|
|
onClicked={() => exec(`/bin/sh -c "kitty --hold fish -c 'fastfetch'"`)}
|
|
child={
|
|
<label label={"View FastFetch"}></label>
|
|
}></button>
|
|
</box>
|
|
);
|
|
};
|
|
|
|
const SystemInformationPanel = () => {
|
|
const popover = new Gtk.Popover();
|
|
|
|
popover.set_child(info());
|
|
|
|
return popover;
|
|
};
|
|
|
|
const sysInfoFetcher = () => {
|
|
if (enabled) {
|
|
if (availableFeatures.cpu) {
|
|
execAsync(`/bin/fish -c cpu-utilization`).then(v => {
|
|
cpuUtil.set("" + Math.round(parseFloat(v)));
|
|
}).catch( e => {
|
|
console.error( e );
|
|
} );
|
|
}
|
|
if (availableFeatures.ram) {
|
|
execAsync(
|
|
`/bin/bash -c "free | awk '/Mem:/ {print $3 \\" \\" $2}'"`,
|
|
).then( v => {
|
|
const util = parseInt( v.split( ' ' )[ 0 ] );
|
|
const available = parseInt( v.split( ' ' )[ 1 ] );
|
|
ramUtil.set( "" + Math.round( available / util ) );
|
|
ramUsed.set( `${Math.round( util / 1024 / 1024 )}MiB used of ${Math.round( available / 1024 / 1024 )}MiB` );
|
|
} ).catch( e => {
|
|
console.error( e );
|
|
} );
|
|
ramUsed.set(
|
|
exec(
|
|
`/bin/bash -c \"free -h | awk '/^Mem:/ {print $3 \\" used of \\" $2}'\"`,
|
|
)
|
|
.replaceAll("Gi", "GiB")
|
|
.replaceAll("Mi", "MiB"),
|
|
);
|
|
}
|
|
gpuUtil.set(exec("cat /sys/class/drm/card1/device/gpu_busy_percent"));
|
|
}
|
|
};
|
|
|
|
const panel = SystemInformationPanel();
|
|
|
|
const SystemInfo = () => {
|
|
featureTest();
|
|
|
|
const openSysInfo = async () => {
|
|
panel.popup();
|
|
systemStats.set(refreshStats());
|
|
};
|
|
|
|
if (enabled) {
|
|
sysInfoFetcher();
|
|
interval(FETCH_INTERVAL, sysInfoFetcher);
|
|
|
|
return (
|
|
<button
|
|
onClicked={() => openSysInfo()}
|
|
child={
|
|
<box tooltipText={ramUsed(v => v)}>
|
|
<image
|
|
iconName={"power-profile-performance-symbolic"}
|
|
marginEnd={1}
|
|
></image>
|
|
<label
|
|
label={cpuUtil(util => util)}
|
|
marginEnd={5}
|
|
></label>
|
|
<image iconName={"histogram-symbolic"}></image>
|
|
<label label={ramUtil(util => util)}></label>
|
|
<image iconName={"show-gpu-effects-symbolic"}></image>
|
|
<label label={gpuUtil(util => util)}></label>
|
|
{panel}
|
|
</box>
|
|
}
|
|
cssClasses={["bar-button"]}
|
|
></button>
|
|
);
|
|
} else {
|
|
return <image iconName={"action-unavailable-symbolic"}></image>;
|
|
}
|
|
};
|
|
|
|
export default {
|
|
SystemInfo,
|
|
panel,
|
|
};
|