Refactor Polling Mechanism: Implement Class-Based Poller with Start/Stop Control (#528)

* custom module updates to class based.

* Finish poller logic.

* Use composition for pollers

* Rename poller

* Handle recorder polling.

* Fix quotes in bash command

* Remove logs
This commit is contained in:
Jas Singh
2024-11-23 03:55:00 -08:00
committed by GitHub
parent c10c9d0e93
commit a4f5fb5917
26 changed files with 460 additions and 169 deletions

View File

@@ -1,77 +0,0 @@
import GLib from 'gi://GLib?version=2.0';
import { GenericFunction } from 'lib/types/customModules/generic';
import { Bind } from 'lib/types/variable';
import { Variable as VariableType } from 'types/variable';
/**
* @param {VariableType<T>} targetVariable - The Variable to update with the function's result.
* @param {Array<Bind>} trackers - Array of trackers to watch.
* @param {Bind} pollingInterval - The polling interval in milliseconds.
* @param {GenericFunction<T, P>} someFunc - The function to execute at each interval, which updates the Variable.
* @param {...P} params - Parameters to pass to someFunc.
*/
export const pollVariable = <T, P extends unknown[], F extends GenericFunction<T, P>>(
targetVariable: VariableType<T>,
trackers: Array<Bind>,
pollingInterval: Bind,
someFunc: F,
...params: P
): void => {
let intervalInstance: number | null = null;
const intervalFn = (pollIntrvl: number): void => {
if (intervalInstance !== null) {
GLib.source_remove(intervalInstance);
}
intervalInstance = Utils.interval(pollIntrvl, () => {
targetVariable.value = someFunc(...params);
});
};
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
intervalFn(pollIntrvl);
});
};
/**
* @param {VariableType<T>} targetVariable - The Variable to update with the result of the command.
* @param {Array<Bind>} trackers - Array of trackers to watch.
* @param {Bind} pollingInterval - The polling interval in milliseconds.
* @param {string} someCommand - The bash command to execute.
* @param {GenericFunction<T, [unknown, ...P]>} someFunc - The function to execute after processing the command result;
* with the first argument being the result of the command execution.
* @param {...P} params - Additional parameters to pass to someFunc.
*/
export const pollVariableBash = <T, P extends unknown[], F extends GenericFunction<T, [string, ...P]>>(
targetVariable: VariableType<T>,
trackers: Array<Bind>,
pollingInterval: Bind,
someCommand: string,
someFunc: F,
...params: P
): void => {
let intervalInstance: number | null = null;
const intervalFn = (pollIntrvl: number): void => {
if (intervalInstance !== null) {
GLib.source_remove(intervalInstance);
}
intervalInstance = Utils.interval(pollIntrvl, () => {
Utils.execAsync(`bash -c "${someCommand}"`)
.then((res: string) => {
try {
targetVariable.value = someFunc(res, ...params);
} catch (error) {
console.warn(`An error occurred when running interval bash function: ${error}`);
}
})
.catch((err) => console.error(`Error running command "${someCommand}": ${err}`));
});
};
Utils.merge([pollingInterval, ...trackers], (pollIntrvl: number) => {
intervalFn(pollIntrvl);
});
};

View File

@@ -1,16 +1,14 @@
import options from 'options';
// Module initializer
import { module } from '../module';
import options from 'options';
import Button from 'types/widgets/button';
// Utility Methods
import { inputHandler } from 'customModules/utils';
import { computeCPU } from './computeCPU';
import { pollVariable } from 'customModules/PollVar';
import { BarBoxChild } from 'lib/types/bar';
import { Attribute, Child } from 'lib/types/widget';
import { FunctionPoller } from 'lib/poller/FunctionPoller';
// All the user configurable options for the cpu module that are needed
const { label, round, leftClick, rightClick, middleClick, scrollUp, scrollDown, pollingInterval, icon } =
@@ -18,7 +16,8 @@ const { label, round, leftClick, rightClick, middleClick, scrollUp, scrollDown,
export const cpuUsage = Variable(0);
pollVariable(
// Instantiate the Poller class for CPU usage polling
const cpuPoller = new FunctionPoller<number, []>(
// Variable to poll and update with the result of the function passed in
cpuUsage,
// Variables that should trigger the polling function to update when they change
@@ -29,6 +28,8 @@ pollVariable(
computeCPU,
);
cpuPoller.initialize('cpu');
export const Cpu = (): BarBoxChild => {
const renderLabel = (cpuUsg: number, rnd: boolean): string => {
return rnd ? `${Math.round(cpuUsg)}%` : `${cpuUsg.toFixed(2)}%`;

View File

@@ -2,14 +2,14 @@ import GLib from 'gi://GLib?version=2.0';
import { convertCelsiusToFahrenheit } from 'globals/weather';
import { UnitType } from 'lib/types/weather';
import options from 'options';
import { Variable } from 'types/variable';
import { Variable as VariableType } from 'types/variable';
const { sensor } = options.bar.customModules.cpuTemp;
/**
* Retrieves the current CPU temperature.
* @returns CPU temperature in degrees Celsius
*/
export const getCPUTemperature = (round: Variable<boolean>, unit: Variable<UnitType>): number => {
export const getCPUTemperature = (round: VariableType<boolean>, unit: VariableType<UnitType>): number => {
try {
if (sensor.value.length === 0) {
return 0;
@@ -23,13 +23,13 @@ export const getCPUTemperature = (round: Variable<boolean>, unit: Variable<UnitT
return 0;
}
let decimalTemp = parseInt(tempInfo) / 1000;
let decimalTemp = parseInt(tempInfo, 10) / 1000;
if (unit.value === 'imperial') {
decimalTemp = convertCelsiusToFahrenheit(decimalTemp);
}
return round ? Math.round(decimalTemp) : parseFloat(decimalTemp.toFixed(2));
return round.value ? Math.round(decimalTemp) : parseFloat(decimalTemp.toFixed(2));
} catch (error) {
console.error('Error calculating CPU Temp:', error);
return 0;

View File

@@ -8,9 +8,11 @@ import Button from 'types/widgets/button';
// Utility Methods
import { inputHandler } from 'customModules/utils';
import { getCPUTemperature } from './helpers';
import { pollVariable } from 'customModules/PollVar';
import { BarBoxChild } from 'lib/types/bar';
import { Attribute, Child } from 'lib/types/widget';
import { FunctionPoller } from 'lib/poller/FunctionPoller';
import { Variable as VariableType } from 'types/variable';
import { UnitType } from 'lib/types/weather';
// All the user configurable options for the cpu module that are needed
const {
@@ -30,7 +32,7 @@ const {
export const cpuTemp = Variable(0);
pollVariable(
const cpuTempPoller = new FunctionPoller<number, [VariableType<boolean>, VariableType<UnitType>]>(
// Variable to poll and update with the result of the function passed in
cpuTemp,
// Variables that should trigger the polling function to update when they change
@@ -43,6 +45,8 @@ pollVariable(
unit,
);
cpuTempPoller.initialize('cputemp');
export const CpuTemp = (): BarBoxChild => {
const cpuTempModule = module({
textIcon: icon.bind('value'),

View File

@@ -1,6 +1,6 @@
import { Variable as TVariable } from 'types/variable';
export const isActiveCommand = `bash -c "pgrep -x "hypridle" > /dev/null && echo "yes" || echo "no""`;
export const isActiveCommand = `bash -c "pgrep -x 'hypridle' &>/dev/null && echo 'yes' || echo 'no'"`;
export const isActive = Variable(false);

View File

@@ -5,8 +5,8 @@ import { inputHandler, throttleInput } from 'customModules/utils';
import Button from 'types/widgets/button';
import { Attribute, Child } from 'lib/types/widget';
import { BarBoxChild } from 'lib/types/bar';
import { pollVariable } from 'customModules/PollVar';
import { checkIdleStatus, isActive, toggleIdle } from './helpers';
import { FunctionPoller } from 'lib/poller/FunctionPoller';
const { label, pollingInterval, onIcon, offIcon, onLabel, offLabel, rightClick, middleClick, scrollUp, scrollDown } =
options.bar.customModules.hypridle;
@@ -15,7 +15,14 @@ const dummyVar = Variable(undefined);
checkIdleStatus();
pollVariable(dummyVar, [], pollingInterval.bind('value'), checkIdleStatus);
const idleStatusPoller = new FunctionPoller<undefined, []>(
dummyVar,
[],
pollingInterval.bind('value'),
checkIdleStatus,
);
idleStatusPoller.initialize('hypridle');
const throttledToggleIdle = throttleInput(() => toggleIdle(isActive), 1000);

View File

@@ -4,7 +4,7 @@ import { Variable as TVariable } from 'types/variable';
const { temperature } = options.bar.customModules.hyprsunset;
export const isActiveCommand = `bash -c "pgrep -x "hyprsunset" > /dev/null && echo "yes" || echo "no""`;
export const isActiveCommand = `bash -c "pgrep -x 'hyprsunset' > /dev/null && echo 'yes' || echo 'no'"`;
export const isActive = Variable(false);

View File

@@ -5,8 +5,8 @@ import { inputHandler, throttleInput } from 'customModules/utils';
import Button from 'types/widgets/button';
import { Attribute, Child } from 'lib/types/widget';
import { BarBoxChild } from 'lib/types/bar';
import { pollVariable } from 'customModules/PollVar';
import { checkSunsetStatus, isActive, toggleSunset } from './helpers';
import { FunctionPoller } from 'lib/poller/FunctionPoller';
const {
label,
@@ -26,7 +26,9 @@ const dummyVar = Variable(undefined);
checkSunsetStatus();
pollVariable(dummyVar, [], pollingInterval.bind('value'), checkSunsetStatus);
const sunsetPoller = new FunctionPoller<undefined, []>(dummyVar, [], pollingInterval.bind('value'), checkSunsetStatus);
sunsetPoller.initialize('hyprsunset');
const throttledToggleSunset = throttleInput(() => toggleSunset(isActive), 1000);

View File

@@ -3,13 +3,14 @@ import options from 'options';
import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import { computeNetwork } from './computeNetwork';
import { BarBoxChild, NetstatLabelType } from 'lib/types/bar';
import { BarBoxChild, NetstatLabelType, RateUnit } from 'lib/types/bar';
import Button from 'types/widgets/button';
import { NetworkResourceData } from 'lib/types/customModules/network';
import { NETWORK_LABEL_TYPES } from 'lib/types/defaults/bar';
import { GET_DEFAULT_NETSTAT_DATA } from 'lib/types/defaults/netstat';
import { pollVariable } from 'customModules/PollVar';
import { Attribute, Child } from 'lib/types/widget';
import { FunctionPoller } from 'lib/poller/FunctionPoller';
import { Variable as TVariable } from 'types/variable';
const {
label,
@@ -27,7 +28,10 @@ const {
export const networkUsage = Variable<NetworkResourceData>(GET_DEFAULT_NETSTAT_DATA(rateUnit.value));
pollVariable(
const netstatPoller = new FunctionPoller<
NetworkResourceData,
[round: TVariable<boolean>, interfaceNameVar: TVariable<string>, dataType: TVariable<RateUnit>]
>(
// Variable to poll and update with the result of the function passed in
networkUsage,
// Variables that should trigger the polling function to update when they change
@@ -48,6 +52,8 @@ pollVariable(
rateUnit,
);
netstatPoller.initialize('netstat');
export const Netstat = (): BarBoxChild => {
const renderNetworkLabel = (lblType: NetstatLabelType, network: NetworkResourceData): string => {
switch (lblType) {

View File

@@ -16,8 +16,9 @@ import { BarBoxChild, ResourceLabelType } from 'lib/types/bar';
// Global Constants
import { LABEL_TYPES } from 'lib/types/defaults/bar';
import { pollVariable } from 'customModules/PollVar';
import { Attribute, Child } from 'lib/types/widget';
import { FunctionPoller } from 'lib/poller/FunctionPoller';
import { Variable as TVariable } from 'types/variable';
// All the user configurable options for the ram module that are needed
const { label, labelType, round, leftClick, rightClick, middleClick, pollingInterval, icon } =
@@ -26,7 +27,15 @@ const { label, labelType, round, leftClick, rightClick, middleClick, pollingInte
const defaultRamData: GenericResourceData = { total: 0, used: 0, percentage: 0, free: 0 };
const ramUsage = Variable<GenericResourceData>(defaultRamData);
pollVariable(ramUsage, [round.bind('value')], pollingInterval.bind('value'), calculateRamUsage, round);
const ramPoller = new FunctionPoller<GenericResourceData, [TVariable<boolean>]>(
ramUsage,
[round.bind('value')],
pollingInterval.bind('value'),
calculateRamUsage,
round,
);
ramPoller.initialize('ram');
export const Ram = (): BarBoxChild => {
const ramModule = module({

View File

@@ -1,14 +1,14 @@
import options from 'options';
import { module } from '../module';
import { formatTooltip, inputHandler, renderResourceLabel } from 'customModules/utils';
import { computeStorage } from './computeStorage';
import { BarBoxChild, ResourceLabelType } from 'lib/types/bar';
import { GenericResourceData } from 'lib/types/customModules/generic';
import Button from 'types/widgets/button';
import { LABEL_TYPES } from 'lib/types/defaults/bar';
import { pollVariable } from 'customModules/PollVar';
import { Attribute, Child } from 'lib/types/widget';
import { FunctionPoller } from 'lib/poller/FunctionPoller';
import { Variable as TVariable } from 'types/variable';
const { label, labelType, icon, round, leftClick, rightClick, middleClick, pollingInterval } =
options.bar.customModules.storage;
@@ -17,7 +17,15 @@ const defaultStorageData = { total: 0, used: 0, percentage: 0, free: 0 };
const storageUsage = Variable<GenericResourceData>(defaultStorageData);
pollVariable(storageUsage, [round.bind('value')], pollingInterval.bind('value'), computeStorage, round);
const storagePoller = new FunctionPoller<GenericResourceData, [TVariable<boolean>]>(
storageUsage,
[round.bind('value')],
pollingInterval.bind('value'),
computeStorage,
round,
);
storagePoller.initialize('storage');
export const Storage = (): BarBoxChild => {
const storageModule = module({

View File

@@ -3,10 +3,10 @@ import { module } from '../module';
import { inputHandler } from 'customModules/utils';
import Button from 'types/widgets/button';
import { Variable as VariableType } from 'types/variable';
import { pollVariableBash } from 'customModules/PollVar';
import { Variable as TVariable } from 'types/variable';
import { Attribute, Child } from 'lib/types/widget';
import { BarBoxChild } from 'lib/types/bar';
import { BashPoller } from 'lib/poller/BashPoller';
const {
updateCommand,
@@ -21,7 +21,7 @@ const {
scrollDown,
} = options.bar.customModules.updates;
const pendingUpdates: VariableType<string> = Variable('0');
const pendingUpdates: TVariable<string> = Variable('0');
const postInputUpdater = Variable(true);
const processUpdateCount = (updateCount: string): string => {
@@ -29,7 +29,7 @@ const processUpdateCount = (updateCount: string): string => {
return `${updateCount.padStart(2, '0')}`;
};
pollVariableBash(
const updatesPoller = new BashPoller<string, []>(
pendingUpdates,
[padZero.bind('value'), postInputUpdater.bind('value')],
pollingInterval.bind('value'),
@@ -37,6 +37,8 @@ pollVariableBash(
processUpdateCount,
);
updatesPoller.initialize('updates');
export const Updates = (): BarBoxChild => {
const updatesModule = module({
textIcon: icon.bind('value'),