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,
 | |
| };
 |