From 3698bfe2b35a7943356618811538e3abd31a6eb3 Mon Sep 17 00:00:00 2001 From: Jas Singh Date: Sun, 16 Feb 2025 12:20:29 -0800 Subject: [PATCH] Replaced the underlying structure of Hypridle inhibitor to the Astal Idle Inhibitor module. (#776) * Update hypridle module to be an all-purpose idle inhibitor module using Astal inhibiting. * Fix types and module code. * Update note * Removed lingering comment. * Remove hypridle from dependencies. --- README.md | 5 +- src/cli/commander/commands/utility/index.ts | 38 +++++++++++++ src/components/bar/index.tsx | 1 + .../bar/modules/hypridle/helpers/index.ts | 55 ------------------- src/components/bar/modules/hypridle/index.tsx | 34 +++++------- src/components/bar/utils/helpers.ts | 43 +++++++++------ src/globals.d.ts | 1 + src/globals/utilities.ts | 3 + src/lib/types/customModules/utils.d.ts | 17 ++++-- src/options.ts | 4 +- 10 files changed, 98 insertions(+), 103 deletions(-) delete mode 100644 src/components/bar/modules/hypridle/helpers/index.ts diff --git a/README.md b/README.md index d2a5c9e..a19d44b 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,6 @@ hyprpicker ## To enable hyprland's very own blue light filter hyprsunset -## To enable hyprland's very own idle inhibitor -hypridle - ## To click resource/stat bars in the dashboard and open btop btop @@ -91,7 +88,7 @@ pacman: AUR: ```bash -yay -S --needed aylurs-gtk-shell-git grimblast-git gpu-screen-recorder-git hyprpicker matugen-bin python-gpustat hyprsunset-git hypridle-git +yay -S --needed aylurs-gtk-shell-git grimblast-git gpu-screen-recorder-git hyprpicker matugen-bin python-gpustat hyprsunset-git ``` ### Fedora diff --git a/src/cli/commander/commands/utility/index.ts b/src/cli/commander/commands/utility/index.ts index d7ad3ab..7398725 100644 --- a/src/cli/commander/commands/utility/index.ts +++ b/src/cli/commander/commands/utility/index.ts @@ -71,6 +71,44 @@ export const utilityCommands: Command[] = [ } }, }, + { + name: 'isInhibiting', + aliases: ['isi'], + description: 'Returns the status of the Idle Inhibitor.', + category: 'Utility', + args: [], + handler: (): boolean => { + try { + return idleInhibit.get(); + } catch (error) { + errorHandler(error); + } + }, + }, + { + name: 'idleInhibit', + aliases: ['idi'], + description: 'Enables/Disables the Idle Inhibitor. Toggles the Inhibitor if no parameter is provided.', + category: 'Utility', + args: [ + { + name: 'shouldInhibit', + description: 'The boolean value that enables/disables the inhibitor.', + type: 'boolean', + required: false, + }, + ], + handler: (args: Record): boolean => { + try { + const shouldInhibit = args['shouldInhibit'] ?? !idleInhibit.get(); + idleInhibit.set(Boolean(shouldInhibit)); + + return idleInhibit.get(); + } catch (error) { + errorHandler(error); + } + }, + }, { name: 'migrateConfig', aliases: ['mcfg'], diff --git a/src/components/bar/index.tsx b/src/components/bar/index.tsx index e770c04..5246f98 100644 --- a/src/components/bar/index.tsx +++ b/src/components/bar/index.tsx @@ -127,6 +127,7 @@ export const Bar = (() => { return ( /dev/null && echo 'yes' || echo 'no'"`; - -/** - * A variable to track the active state of the hypridle process. - */ -export const isActive = Variable(false); - -/** - * Updates the active state of the hypridle process. - * - * This function checks if the hypridle process is currently running and updates the `isActive` variable accordingly. - * - * @param isActive A Variable that tracks the active state of the hypridle process. - */ -const updateIsActive = (isActive: Variable): void => { - execAsync(isActiveCommand).then((res) => { - isActive.set(res === 'yes'); - }); -}; - -/** - * Toggles the hypridle process on or off based on its current state. - * - * This function checks if the hypridle process is currently running. If it is not running, it starts the process. - * If it is running, it stops the process. The active state is updated accordingly. - * - * @param isActive A Variable that tracks the active state of the hypridle process. - */ -export const toggleIdle = (isActive: Variable): void => { - execAsync(isActiveCommand).then((res) => { - const toggleIdleCommand = - res === 'no' ? `bash -c "nohup hypridle > /dev/null 2>&1 &"` : `bash -c "pkill hypridle"`; - - execAsync(toggleIdleCommand).then(() => updateIsActive(isActive)); - }); -}; - -/** - * Checks the current status of the hypridle process and updates the active state. - * - * This function checks if the hypridle process is currently running and updates the `isActive` variable accordingly. - */ -export const checkIdleStatus = (): undefined => { - execAsync(isActiveCommand).then((res) => { - isActive.set(res === 'yes'); - }); -}; diff --git a/src/components/bar/modules/hypridle/index.tsx b/src/components/bar/modules/hypridle/index.tsx index 68125c2..2d275f2 100644 --- a/src/components/bar/modules/hypridle/index.tsx +++ b/src/components/bar/modules/hypridle/index.tsx @@ -1,37 +1,33 @@ import options from 'src/options'; import { Module } from '../../shared/Module'; -import { inputHandler, throttleInput } from '../../utils/helpers'; -import { checkIdleStatus, isActive, toggleIdle } from './helpers'; -import { FunctionPoller } from '../../../../lib/poller/FunctionPoller'; +import { inputHandler } from '../../utils/helpers'; import Variable from 'astal/variable'; import { bind } from 'astal'; import { BarBoxChild } from 'src/lib/types/bar'; import { Astal } from 'astal/gtk3'; -const { label, pollingInterval, onIcon, offIcon, onLabel, offLabel, rightClick, middleClick, scrollUp, scrollDown } = +const { label, onIcon, offIcon, onLabel, offLabel, rightClick, middleClick, scrollUp, scrollDown } = options.bar.customModules.hypridle; -const dummyVar = Variable(undefined); - -checkIdleStatus(); - -const idleStatusPoller = new FunctionPoller(dummyVar, [], bind(pollingInterval), checkIdleStatus); - -idleStatusPoller.initialize('hypridle'); - -const throttledToggleIdle = throttleInput(() => toggleIdle(isActive), 1000); +function toggleInhibit(): void { + idleInhibit.set(!idleInhibit.get()); +} export const Hypridle = (): BarBoxChild => { - const iconBinding = Variable.derive([bind(isActive), bind(onIcon), bind(offIcon)], (active, onIcn, offIcn) => { + const iconBinding = Variable.derive([bind(idleInhibit), bind(onIcon), bind(offIcon)], (active, onIcn, offIcn) => { return active ? onIcn : offIcn; }); - const labelBinding = Variable.derive([bind(isActive), bind(onLabel), bind(offLabel)], (active, onLbl, offLbl) => { - return active ? onLbl : offLbl; - }); + + const labelBinding = Variable.derive( + [bind(idleInhibit), bind(onLabel), bind(offLabel)], + (active, onLbl, offLbl) => { + return active ? onLbl : offLbl; + }, + ); const hypridleModule = Module({ textIcon: iconBinding(), - tooltipText: bind(isActive).as((active) => `Hypridle ${active ? 'enabled' : 'disabled'}`), + tooltipText: bind(idleInhibit).as((active) => `Idle Inhibitor: ${active ? 'Enabled' : 'Disabled'}`), boxClass: 'hypridle', label: labelBinding(), showLabelBinding: bind(label), @@ -40,7 +36,7 @@ export const Hypridle = (): BarBoxChild => { inputHandler(self, { onPrimaryClick: { fn: () => { - throttledToggleIdle(); + toggleInhibit(); }, }, onSecondaryClick: { diff --git a/src/components/bar/utils/helpers.ts b/src/components/bar/utils/helpers.ts index 349b10e..ea4ee05 100644 --- a/src/components/bar/utils/helpers.ts +++ b/src/components/bar/utils/helpers.ts @@ -1,6 +1,6 @@ import { ResourceLabelType } from 'src/lib/types/bar'; import { GenericResourceData, Postfix, UpdateHandlers } from 'src/lib/types/customModules/generic'; -import { InputHandlerEvents, RunAsyncCommand } from 'src/lib/types/customModules/utils'; +import { InputHandlerEventArgs, InputHandlerEvents, RunAsyncCommand } from 'src/lib/types/customModules/utils'; import { ThrottleFn } from 'src/lib/types/utils'; import { bind, Binding, execAsync, Variable } from 'astal'; import { openMenu } from 'src/components/bar/utils/menu'; @@ -56,6 +56,15 @@ export const runAsyncCommand: RunAsyncCommand = (cmd, events, fn, postInputUpdat .catch((err) => console.error(`Error running command "${cmd}": ${err})`)); }; +/* + * NOTE: Added a throttle since spamming a button yields duplicate events + * which undo the toggle. + */ +const throttledAsyncCommand = throttleInput( + (cmd, events, fn, postInputUpdater?: Variable) => runAsyncCommand(cmd, events, fn, postInputUpdater), + 50, +); + /** * Generic throttle function to limit the rate at which a function can be called. * @@ -90,7 +99,7 @@ export function throttleInput(func: T, limit: number): T { */ export const throttledScrollHandler = (interval: number): ThrottleFn => throttleInput((cmd: string, args, fn, postInputUpdater) => { - runAsyncCommand(cmd, args, fn, postInputUpdater); + throttledAsyncCommand(cmd, args, fn, postInputUpdater); }, 200 / interval); /** @@ -114,7 +123,7 @@ export const inputHandler = ( }: InputHandlerEvents, postInputUpdater?: Variable, ): void => { - const sanitizeInput = (input: Variable): string => { + const sanitizeInput = (input?: Variable | Variable): string => { if (input === undefined) { return ''; } @@ -126,34 +135,34 @@ export const inputHandler = ( const throttledHandler = throttledScrollHandler(interval); const disconnectPrimaryClick = onPrimaryClick(self, (clicked: GtkWidget, event: Gdk.Event) => { - runAsyncCommand( + throttledAsyncCommand( sanitizeInput(onPrimaryClickInput?.cmd || dummyVar), { clicked, event }, - onPrimaryClickInput.fn, + onPrimaryClickInput?.fn, postInputUpdater, ); }); const disconnectSecondaryClick = onSecondaryClick(self, (clicked: GtkWidget, event: Gdk.Event) => { - runAsyncCommand( + throttledAsyncCommand( sanitizeInput(onSecondaryClickInput?.cmd || dummyVar), { clicked, event }, - onSecondaryClickInput.fn, + onSecondaryClickInput?.fn, postInputUpdater, ); }); const disconnectMiddleClick = onMiddleClick(self, (clicked: GtkWidget, event: Gdk.Event) => { - runAsyncCommand( + throttledAsyncCommand( sanitizeInput(onMiddleClickInput?.cmd || dummyVar), { clicked, event }, - onMiddleClickInput.fn, + onMiddleClickInput?.fn, postInputUpdater, ); }); const id = self.connect('scroll-event', (self: GtkWidget, event: Gdk.Event) => { - const handleScroll = (input?: { cmd: Variable; fn: (output: string) => void }): void => { + const handleScroll = (input?: InputHandlerEventArgs): void => { if (input) { throttledHandler(sanitizeInput(input.cmd), { clicked: self, event }, input.fn, postInputUpdater); } @@ -178,8 +187,8 @@ export const inputHandler = ( updateHandlers(); - const sanitizeVariable = (someVar: Variable | undefined): Binding => { - if (someVar === undefined || typeof someVar.bind !== 'function') { + const sanitizeVariable = (someVar?: Variable): Binding => { + if (someVar === undefined) { return bind(dummyVar); } return bind(someVar); @@ -188,11 +197,11 @@ export const inputHandler = ( Variable.derive( [ bind(scrollSpeed), - sanitizeVariable(onPrimaryClickInput), - sanitizeVariable(onSecondaryClickInput), - sanitizeVariable(onMiddleClickInput), - sanitizeVariable(onScrollUpInput), - sanitizeVariable(onScrollDownInput), + sanitizeVariable(onPrimaryClickInput?.cmd), + sanitizeVariable(onSecondaryClickInput?.cmd), + sanitizeVariable(onMiddleClickInput?.cmd), + sanitizeVariable(onScrollUpInput?.cmd), + sanitizeVariable(onScrollDownInput?.cmd), ], () => { const handlers = updateHandlers(); diff --git a/src/globals.d.ts b/src/globals.d.ts index 92c2d98..15e7e17 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -15,6 +15,7 @@ declare global { var globalWeatherVar: Variable; var options: Options; var removingNotifications: Variable; + var idleInhibit: Variable; } export {}; diff --git a/src/globals/utilities.ts b/src/globals/utilities.ts index 728f581..f5e80ea 100644 --- a/src/globals/utilities.ts +++ b/src/globals/utilities.ts @@ -1,6 +1,7 @@ import { App } from 'astal/gtk3'; import options from '../options'; import { BarLayouts } from 'src/lib/types/options'; +import { Variable } from 'astal'; globalThis.isWindowVisible = (windowName: string): boolean => { const appWindow = App.get_window(windowName); @@ -22,3 +23,5 @@ globalThis.setLayout = (layout: BarLayouts): string => { return `Failed to set layout: ${error}`; } }; + +globalThis.idleInhibit = Variable(false); diff --git a/src/lib/types/customModules/utils.d.ts b/src/lib/types/customModules/utils.d.ts index 0bb5259..a6cef6d 100644 --- a/src/lib/types/customModules/utils.d.ts +++ b/src/lib/types/customModules/utils.d.ts @@ -1,12 +1,17 @@ +import { Variable } from 'astal'; +import { Opt } from 'src/lib/option'; import { Binding } from 'src/lib/utils'; -import { Variable } from 'types/variable'; +export type InputHandlerEventArgs = { + cmd?: Opt | Variable; + fn?: (output: string) => void; +}; export type InputHandlerEvents = { - onPrimaryClick?: Binding; - onSecondaryClick?: Binding; - onMiddleClick?: Binding; - onScrollUp?: Binding; - onScrollDown?: Binding; + onPrimaryClick?: InputHandlerEventArgs; + onSecondaryClick?: InputHandlerEventArgs; + onMiddleClick?: InputHandlerEventArgs; + onScrollUp?: InputHandlerEventArgs; + onScrollDown?: InputHandlerEventArgs; }; export type RunAsyncCommand = ( diff --git a/src/options.ts b/src/options.ts index 4b20f0a..1b72ef8 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1172,8 +1172,8 @@ const options = mkOptions(CONFIG, { }, hypridle: { label: opt(true), - onIcon: opt(''), - offIcon: opt(''), + onIcon: opt('󰒳'), + offIcon: opt('󰒲'), onLabel: opt('On'), offLabel: opt('Off'), pollingInterval: opt(1000 * 2),