136 lines
4.3 KiB
TypeScript
136 lines
4.3 KiB
TypeScript
import { exec, GLib, interval, Variable } from "astal"
|
|
import { Gtk } from "astal/gtk4";
|
|
import AstalBattery from "gi://AstalBattery?version=0.1";
|
|
|
|
|
|
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' ),
|
|
netSpeed: exec( `/bin/bash -c "interface=$(ip route get 8.8.8.8 | awk '{print $5; exit}') && cat \"/sys/class/net/$interface/speed\""` ),
|
|
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 valign={Gtk.Align.START}>
|
|
<label label={ramUsed( used => {
|
|
return used + `(${ ramUtil.get() }%)`;
|
|
} )}></label>
|
|
<label label={systemStats( stats => {
|
|
return `CPU: ${stats.cpuTemp}, ${stats.cpuClk}
|
|
GPU: ${stats.gpuTemp}, ${stats.gpuClk} (${stats.vram} / ${stats.availableVRAM})
|
|
Network: ${stats.netSpeed}
|
|
Kernel: ${stats.kernel}` } ) }></label>
|
|
</box>;
|
|
}
|
|
|
|
|
|
const SystemInformationPanel = () => {
|
|
const popover = new Gtk.Popover();
|
|
|
|
popover.set_child( info() );
|
|
|
|
return popover;
|
|
}
|
|
|
|
|
|
const sysInfoFetcher = () => {
|
|
if ( enabled ) {
|
|
if ( availableFeatures.cpu ) {
|
|
cpuUtil.set( '' + Math.round( parseFloat( exec( `/bin/fish -c cpu-utilization` ) ) ) );
|
|
}
|
|
if ( availableFeatures.ram ) {
|
|
ramUtil.set( '' + Math.round( parseFloat( exec( `/bin/bash -c "free | awk '/Mem/ { printf(\\"%.2f\\\\n\\", ($3/$2)*100) }'"` ) ) ) );
|
|
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>
|
|
}></button>
|
|
} else {
|
|
return <image iconName={"action-unavailable-symbolic"}></image>
|
|
}
|
|
}
|
|
|
|
|
|
export default {
|
|
SystemInfo,
|
|
panel
|
|
}
|