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:
@@ -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);
|
||||
});
|
||||
};
|
||||
@@ -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)}%`;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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'),
|
||||
|
||||
1
globals.d.ts
vendored
1
globals.d.ts
vendored
@@ -1,4 +1,3 @@
|
||||
// globals.d.ts
|
||||
/* eslint-disable no-var */
|
||||
|
||||
import { Options, Variable as VariableType } from 'types/variable';
|
||||
|
||||
@@ -10,8 +10,6 @@ globalThis.isWindowVisible = (windowName: string): boolean => {
|
||||
};
|
||||
|
||||
globalThis.setLayout = (layout: string): string => {
|
||||
console.log(layout);
|
||||
|
||||
try {
|
||||
const layoutJson = JSON.parse(layout);
|
||||
const { layouts } = options.bar;
|
||||
|
||||
90
lib/poller/BashPoller.ts
Normal file
90
lib/poller/BashPoller.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
import { Bind } from 'lib/types/variable';
|
||||
import { GenericFunction } from 'lib/types/customModules/generic';
|
||||
import { BarModule } from 'lib/types/options';
|
||||
import { Poller } from './Poller';
|
||||
|
||||
/**
|
||||
* A class that manages polling of a variable by executing a bash command at specified intervals.
|
||||
*/
|
||||
export class BashPoller<Value, Parameters extends unknown[]> {
|
||||
private poller: Poller;
|
||||
|
||||
private params: Parameters;
|
||||
|
||||
/**
|
||||
* Creates an instance of BashPoller.
|
||||
*
|
||||
* @param targetVariable - The target variable to poll.
|
||||
* @param trackers - An array of trackers to monitor.
|
||||
* @param pollingInterval - The interval at which polling occurs.
|
||||
* @param updateCommand - The command to update the target variable.
|
||||
* @param pollingFunction - The function to execute during each poll.
|
||||
* @param params - Additional parameters for the polling function.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* //##################### EXAMPLE ##########################
|
||||
* const updatesPoller = new BashPoller<string, []>(
|
||||
* pendingUpdates,
|
||||
* [padZero.bind('value'), postInputUpdater.bind('value')],
|
||||
* pollingInterval.bind('value'),
|
||||
* updateCommand.value,
|
||||
* processUpdateCount,
|
||||
* );
|
||||
* //#######################################################
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
constructor(
|
||||
private targetVariable: VariableType<Value>,
|
||||
private trackers: Bind[],
|
||||
private pollingInterval: Bind,
|
||||
private updateCommand: string,
|
||||
private pollingFunction: GenericFunction<Value, [string, ...Parameters]>,
|
||||
...params: Parameters
|
||||
) {
|
||||
this.params = params;
|
||||
|
||||
this.poller = new Poller(this.pollingInterval, this.trackers, this.execute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the bash command specified in the updateCommand property.
|
||||
*
|
||||
* The result of the command is processed by the pollingFunction and
|
||||
* assigned to the targetVariable.
|
||||
*/
|
||||
public execute = async (): Promise<void> => {
|
||||
try {
|
||||
const res = await Utils.execAsync(`bash -c "${this.updateCommand}"`);
|
||||
this.targetVariable.value = await this.pollingFunction(res, ...this.params);
|
||||
} catch (error) {
|
||||
console.error(`Error executing bash command "${this.updateCommand}":`, error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts the polling process.
|
||||
*/
|
||||
public start(): void {
|
||||
this.poller.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the polling process.
|
||||
*/
|
||||
public stop(): void {
|
||||
this.poller.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the poller with the specified module.
|
||||
*
|
||||
* @param moduleName - The name of the module to initialize.
|
||||
*/
|
||||
public initialize(moduleName?: BarModule): void {
|
||||
this.poller.initialize(moduleName);
|
||||
}
|
||||
}
|
||||
86
lib/poller/FunctionPoller.ts
Normal file
86
lib/poller/FunctionPoller.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Variable as VariableType } from 'types/variable';
|
||||
import { Bind } from 'lib/types/variable';
|
||||
import { GenericFunction } from 'lib/types/customModules/generic';
|
||||
import { BarModule } from 'lib/types/options';
|
||||
import { Poller } from './Poller';
|
||||
|
||||
/**
|
||||
* A class that manages polling of a variable by executing a generic function at specified intervals.
|
||||
*/
|
||||
export class FunctionPoller<Value, Parameters extends unknown[] = []> {
|
||||
private poller: Poller;
|
||||
|
||||
private params: Parameters;
|
||||
|
||||
/**
|
||||
* Creates an instance of FunctionPoller.
|
||||
*
|
||||
* @param targetVariable - The target variable to poll.
|
||||
* @param trackers - An array of trackers to monitor.
|
||||
* @param pollingInterval - The interval at which polling occurs.
|
||||
* @param pollingFunction - The function to execute during each poll.
|
||||
* @param params - Additional parameters for the polling function.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* //##################### EXAMPLE ##########################
|
||||
* const cpuPoller = new FunctionPoller<number, []>(
|
||||
* cpuUsage,
|
||||
* [round.bind('value')],
|
||||
* pollingInterval.bind('value'),
|
||||
* computeCPU,
|
||||
* );
|
||||
* //#######################################################
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
constructor(
|
||||
private targetVariable: VariableType<Value>,
|
||||
private trackers: Bind[],
|
||||
private pollingInterval: Bind,
|
||||
private pollingFunction: GenericFunction<Value, Parameters>,
|
||||
...params: Parameters
|
||||
) {
|
||||
this.params = params;
|
||||
|
||||
this.poller = new Poller(this.pollingInterval, this.trackers, this.execute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the polling function with the provided parameters.
|
||||
*
|
||||
* The result of the function is assigned to the target variable.
|
||||
*/
|
||||
private execute = async (): Promise<void> => {
|
||||
try {
|
||||
const result = await this.pollingFunction(...this.params);
|
||||
this.targetVariable.value = result;
|
||||
} catch (error) {
|
||||
console.error('Error executing polling function:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts the polling process.
|
||||
*/
|
||||
public start(): void {
|
||||
this.poller.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the polling process.
|
||||
*/
|
||||
public stop(): void {
|
||||
this.poller.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the poller with the specified module.
|
||||
*
|
||||
* @param moduleName - The name of the module to initialize.
|
||||
*/
|
||||
public initialize(moduleName?: BarModule): void {
|
||||
this.poller.initialize(moduleName);
|
||||
}
|
||||
}
|
||||
107
lib/poller/Poller.ts
Normal file
107
lib/poller/Poller.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import GLib from 'gi://GLib?version=2.0';
|
||||
import { Bind } from 'lib/types/variable';
|
||||
import { BarModule } from 'lib/types/options';
|
||||
import { getLayoutItems } from 'lib/utils';
|
||||
|
||||
const { layouts } = options.bar;
|
||||
|
||||
/**
|
||||
* A class that manages the polling lifecycle, including interval management and execution state.
|
||||
*/
|
||||
export class Poller {
|
||||
private intervalInstance: number | null = null;
|
||||
private isExecuting: boolean = false;
|
||||
private pollingFunction: () => Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates an instance of Poller.
|
||||
* @param pollingInterval - The interval at which polling occurs.
|
||||
* @param trackers - An array of trackers to monitor.
|
||||
* @param pollingFunction - The function to execute during each poll.
|
||||
*/
|
||||
constructor(
|
||||
private pollingInterval: Bind,
|
||||
private trackers: Bind[],
|
||||
pollingFunction: () => Promise<void>,
|
||||
) {
|
||||
this.pollingFunction = pollingFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the polling process by setting up the interval.
|
||||
*/
|
||||
public start(): void {
|
||||
Utils.merge([this.pollingInterval, ...this.trackers], (intervalMs: number) => {
|
||||
this.executePolling(intervalMs);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the polling process and cleans up resources.
|
||||
*/
|
||||
public stop(): void {
|
||||
if (this.intervalInstance !== null) {
|
||||
GLib.source_remove(this.intervalInstance);
|
||||
this.intervalInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the polling based on module usage.
|
||||
*
|
||||
* If not module is provided then we can safely assume that we want
|
||||
* to always run the pollig interval.
|
||||
*
|
||||
* @param moduleName - The name of the module to initialize.
|
||||
*/
|
||||
public initialize(moduleName?: BarModule): void {
|
||||
if (moduleName === undefined) {
|
||||
return this.start();
|
||||
}
|
||||
|
||||
const initialModules = getLayoutItems();
|
||||
|
||||
if (initialModules.includes(moduleName)) {
|
||||
this.start();
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
layouts.connect('changed', () => {
|
||||
const usedModules = getLayoutItems();
|
||||
|
||||
if (usedModules.includes(moduleName)) {
|
||||
this.start();
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the polling function at the specified interval.
|
||||
*
|
||||
* @param intervalMs - The polling interval in milliseconds.
|
||||
*/
|
||||
private executePolling(intervalMs: number): void {
|
||||
if (this.intervalInstance !== null) {
|
||||
GLib.source_remove(this.intervalInstance);
|
||||
}
|
||||
|
||||
this.intervalInstance = Utils.interval(intervalMs, async () => {
|
||||
if (this.isExecuting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isExecuting = true;
|
||||
|
||||
try {
|
||||
await this.pollingFunction();
|
||||
} catch (error) {
|
||||
console.error('Error during polling execution:', error);
|
||||
} finally {
|
||||
this.isExecuting = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
2
lib/types/customModules/generic.d.ts
vendored
2
lib/types/customModules/generic.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
export type GenericFunction<T, P extends unknown[] = unknown[]> = (...args: P) => T;
|
||||
export type GenericFunction<Value, Parameters extends unknown[]> = (...args: Parameters) => Promise<Value> | Value;
|
||||
|
||||
export type GenericResourceMetrics = {
|
||||
total: number;
|
||||
|
||||
32
lib/types/options.d.ts
vendored
32
lib/types/options.d.ts
vendored
@@ -15,8 +15,38 @@ export type RecursiveOptionsObject = {
|
||||
};
|
||||
|
||||
export type BarLocation = 'top' | 'bottom';
|
||||
export type BarModule =
|
||||
| 'battery'
|
||||
| 'dashboard'
|
||||
| 'workspaces'
|
||||
| 'windowtitle'
|
||||
| 'media'
|
||||
| 'notifications'
|
||||
| 'volume'
|
||||
| 'network'
|
||||
| 'bluetooth'
|
||||
| 'clock'
|
||||
| 'ram'
|
||||
| 'cpu'
|
||||
| 'cputemp'
|
||||
| 'storage'
|
||||
| 'netstat'
|
||||
| 'kbinput'
|
||||
| 'updates'
|
||||
| 'submap'
|
||||
| 'weather'
|
||||
| 'power'
|
||||
| 'systray'
|
||||
| 'hypridle'
|
||||
| 'hyprsunset';
|
||||
|
||||
export type BarLayout = {
|
||||
[key: string]: Layout;
|
||||
left: BarModule[];
|
||||
middle: BarModule[];
|
||||
right: BarModule[];
|
||||
};
|
||||
export type BarLayouts = {
|
||||
[key: string]: BarLayout;
|
||||
};
|
||||
|
||||
export type Unit = 'imperial' | 'metric';
|
||||
|
||||
25
lib/utils.ts
25
lib/utils.ts
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { type Application } from 'types/service/applications';
|
||||
import { NotificationAnchor } from './types/options';
|
||||
import { BarModule, NotificationAnchor } from './types/options';
|
||||
import { OSDAnchor } from 'lib/types/options';
|
||||
import icons, { substitutes } from './icons';
|
||||
import Gtk from 'gi://Gtk?version=3.0';
|
||||
@@ -18,6 +18,29 @@ import options from 'options';
|
||||
|
||||
export type Binding<T> = import('types/service').Binding<any, any, T>;
|
||||
|
||||
/**
|
||||
* Retrieves all unique layout items from the bar options.
|
||||
*
|
||||
* @returns An array of unique layout items.
|
||||
*/
|
||||
export const getLayoutItems = (): BarModule[] => {
|
||||
const { layouts } = options.bar;
|
||||
|
||||
const itemsInLayout: BarModule[] = [];
|
||||
|
||||
Object.keys(layouts.value).forEach((monitor) => {
|
||||
const leftItems = layouts.value[monitor].left;
|
||||
const rightItems = layouts.value[monitor].right;
|
||||
const middleItems = layouts.value[monitor].middle;
|
||||
|
||||
itemsInLayout.push(...leftItems);
|
||||
itemsInLayout.push(...middleItems);
|
||||
itemsInLayout.push(...rightItems);
|
||||
});
|
||||
|
||||
return [...new Set(itemsInLayout)];
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns substitute icon || name || fallback icon
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,7 @@ import Button from 'types/widgets/button.js';
|
||||
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js';
|
||||
|
||||
import './SideEffects';
|
||||
import { BarLayout, WindowLayer } from 'lib/types/options.js';
|
||||
import { BarLayout, BarLayouts, WindowLayer } from 'lib/types/options.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
import Window from 'types/widgets/window.js';
|
||||
|
||||
@@ -43,40 +43,7 @@ const { layouts } = options.bar;
|
||||
const { location } = options.theme.bar;
|
||||
const { location: borderLocation } = options.theme.bar.border;
|
||||
|
||||
export type BarWidget = keyof typeof widget;
|
||||
|
||||
type Section =
|
||||
| 'battery'
|
||||
| 'dashboard'
|
||||
| 'workspaces'
|
||||
| 'windowtitle'
|
||||
| 'media'
|
||||
| 'notifications'
|
||||
| 'volume'
|
||||
| 'network'
|
||||
| 'bluetooth'
|
||||
| 'clock'
|
||||
| 'ram'
|
||||
| 'cpu'
|
||||
| 'cputemp'
|
||||
| 'storage'
|
||||
| 'netstat'
|
||||
| 'kbinput'
|
||||
| 'updates'
|
||||
| 'submap'
|
||||
| 'weather'
|
||||
| 'power'
|
||||
| 'systray'
|
||||
| 'hypridle'
|
||||
| 'hyprsunset';
|
||||
|
||||
type Layout = {
|
||||
left: Section[];
|
||||
middle: Section[];
|
||||
right: Section[];
|
||||
};
|
||||
|
||||
const getLayoutForMonitor = (monitor: number, layouts: BarLayout): Layout => {
|
||||
const getLayoutForMonitor = (monitor: number, layouts: BarLayouts): BarLayout => {
|
||||
const matchingKey = Object.keys(layouts).find((key) => key === monitor.toString());
|
||||
const wildcard = Object.keys(layouts).find((key) => key === '*');
|
||||
|
||||
@@ -95,7 +62,7 @@ const getLayoutForMonitor = (monitor: number, layouts: BarLayout): Layout => {
|
||||
};
|
||||
};
|
||||
|
||||
const isLayoutEmpty = (layout: Layout): boolean => {
|
||||
const isLayoutEmpty = (layout: BarLayout): boolean => {
|
||||
const isLeftSectionEmpty = !Array.isArray(layout.left) || layout.left.length === 0;
|
||||
const isRightSectionEmpty = !Array.isArray(layout.right) || layout.right.length === 0;
|
||||
const isMiddleSectionEmpty = !Array.isArray(layout.middle) || layout.middle.length === 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const hyprland = await Service.import('hyprland');
|
||||
import { BashPoller } from 'lib/poller/BashPoller';
|
||||
import { Attribute, BoxWidget, Child } from 'lib/types/widget';
|
||||
import options from 'options';
|
||||
import { Variable as VarType } from 'types/variable';
|
||||
@@ -9,18 +10,26 @@ import Label from 'types/widgets/label';
|
||||
const { left, right } = options.menus.dashboard.shortcuts;
|
||||
|
||||
const Shortcuts = (): BoxWidget => {
|
||||
const isRecording = Variable(false, {
|
||||
poll: [
|
||||
1000,
|
||||
`${App.configDir}/services/screen_record.sh status`,
|
||||
(out): boolean => {
|
||||
if (out === 'recording') {
|
||||
const pollingInterval = Variable(1000);
|
||||
const isRecording = Variable(false);
|
||||
|
||||
const handleRecorder = (commandOutput: string): boolean => {
|
||||
if (commandOutput === 'recording') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const recordingPoller = new BashPoller<boolean, []>(
|
||||
isRecording,
|
||||
[],
|
||||
pollingInterval.bind('value'),
|
||||
`${App.configDir}/services/screen_record.sh status`,
|
||||
handleRecorder,
|
||||
);
|
||||
|
||||
recordingPoller.initialize();
|
||||
|
||||
const handleClick = (action: string, tOut: number = 250): void => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { KbLabelType } from 'lib/types/customModules/kbLayout';
|
||||
import {
|
||||
ActiveWsIndicator,
|
||||
BarButtonStyles,
|
||||
BarLayout,
|
||||
BarLayouts,
|
||||
BarLocation,
|
||||
BluetoothBatteryState,
|
||||
BorderLocation,
|
||||
@@ -872,7 +872,7 @@ const options = mkOptions(OPTIONS, {
|
||||
|
||||
bar: {
|
||||
scrollSpeed: opt(5),
|
||||
layouts: opt<BarLayout>({
|
||||
layouts: opt<BarLayouts>({
|
||||
'1': {
|
||||
left: ['dashboard', 'workspaces', 'windowtitle'],
|
||||
middle: ['media'],
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
// @ts-expect-error: This import is a special directive that tells the compiler to use the GTop library
|
||||
import GTop from 'gi://GTop';
|
||||
|
||||
import { pollVariable } from 'customModules/PollVar';
|
||||
import { FunctionPoller } from 'lib/poller/FunctionPoller';
|
||||
|
||||
class Cpu {
|
||||
private updateFrequency = Variable(2000);
|
||||
@@ -15,7 +14,14 @@ class Cpu {
|
||||
GTop.glibtop_get_cpu(this.previousCpuData);
|
||||
|
||||
this.calculateUsage = this.calculateUsage.bind(this);
|
||||
pollVariable(this.cpu, [], this.updateFrequency.bind('value'), this.calculateUsage);
|
||||
const cpuPoller = new FunctionPoller<number, []>(
|
||||
this.cpu,
|
||||
[],
|
||||
this.updateFrequency.bind('value'),
|
||||
this.calculateUsage,
|
||||
);
|
||||
|
||||
cpuPoller.start();
|
||||
}
|
||||
|
||||
public calculateUsage(): number {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
|
||||
import { pollVariable } from 'customModules/PollVar';
|
||||
import { FunctionPoller } from 'lib/poller/FunctionPoller';
|
||||
import { GenericResourceData } from 'lib/types/customModules/generic';
|
||||
|
||||
class Ram {
|
||||
@@ -13,7 +13,14 @@ class Ram {
|
||||
|
||||
constructor() {
|
||||
this.calculateUsage = this.calculateUsage.bind(this);
|
||||
pollVariable(this.ram, [], this.updateFrequency.bind('value'), this.calculateUsage);
|
||||
const ramPoller = new FunctionPoller<GenericResourceData, []>(
|
||||
this.ram,
|
||||
[],
|
||||
this.updateFrequency.bind('value'),
|
||||
this.calculateUsage,
|
||||
);
|
||||
|
||||
ramPoller.start();
|
||||
}
|
||||
|
||||
public calculateUsage(): GenericResourceData {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// @ts-expect-error: This import is a special directive that tells the compiler to use the GTop library
|
||||
import GTop from 'gi://GTop';
|
||||
|
||||
import { pollVariable } from 'customModules/PollVar';
|
||||
import { FunctionPoller } from 'lib/poller/FunctionPoller';
|
||||
import { GenericResourceData } from 'lib/types/customModules/generic';
|
||||
|
||||
class Storage {
|
||||
@@ -14,7 +14,14 @@ class Storage {
|
||||
|
||||
constructor() {
|
||||
this.calculateUsage = this.calculateUsage.bind(this);
|
||||
pollVariable(this.storage, [], this.updateFrequency.bind('value'), this.calculateUsage);
|
||||
const storagePoller = new FunctionPoller<GenericResourceData, []>(
|
||||
this.storage,
|
||||
[],
|
||||
this.updateFrequency.bind('value'),
|
||||
this.calculateUsage,
|
||||
);
|
||||
|
||||
storagePoller.start();
|
||||
}
|
||||
|
||||
public calculateUsage(): GenericResourceData {
|
||||
|
||||
Reference in New Issue
Block a user