Add while_eq macro: variable-driven loop stoppable from another mapping - #1352
Open
timcharper wants to merge 1 commit into
Open
timcharper wants to merge 1 commit into
timcharper wants to merge 1 commit into
Conversation
…her mapping Motivation: an autoclicker toggled on by one key and off by a different key. `toggle()` only stops via a second press of the *same* trigger, so a separate 'clear everything' key can't reach it. The natural workaround, `repeat(N, if_eq($var, 1, ..., wait(...)))`, has a sharper problem: `RepeatTask.run()` reads its count once and loops a plain `for _ in range(repeats)` with no way to exit early, while `Macro.run()` refuses to start a second execution while the first is still "running" (see the 'Tried to run already running macro' guard). So even after the shared variable is flipped to stop the clicking, the macro instance keeps occupying that slot until the fixed count exhausts — anywhere up to the full repeat budget — silently swallowing every re-press of the trigger key in the meantime. `while_eq(variable, value, macro)` closes that gap: it re-reads the variable every iteration (like `if_eq`) instead of deciding a count up front (like `repeat`), so `run()` actually returns as soon as the condition no longer holds. That both lets a different mapping stop the loop via `set()`/`add()`, and frees the mapping to be retriggered immediately rather than waiting out a stale budget. Includes docs (readme/macros.md, alongside toggle/if_eq) and tests modeled on test_repeat.py/test_set.py covering: no-op when the condition starts false, stopping promptly when a variable written by other code changes mid-loop, and immediate re-triggerability after stopping.
Owner
|
Thanks! Your implementation of while_eq mirrors the deprecated ifeq macro ( I fixed the lint pipeline yesterday, just merge main into your branch |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
I wanted an autoclicker toggled on by one key and stopped by a different
key (e.g. Shift+F4 starts clicking, F4 stops it and also releases a
held button from a third mapping).
toggle()doesn't fit: it onlystarts/stops on a second press of the same trigger, so an unrelated
key has no way to reach it.
The natural workaround is
repeat(N, if_eq($var, 1, click, wait))drivenby a shared
set()/if_eq()variable — but this runs into a sharperproblem than just "clicking never stops immediately".
RepeatTask.run()reads its count once and loops a plain
for _ in range(repeats), with noway to exit early:
https://github.com/sezanzeb/input-remapper/blob/2.2.1/inputremapper/injection/macros/tasks/repeat.py#L44-L49
Meanwhile
Macro.run()refuses to start a second execution while thefirst is still "running":
https://github.com/sezanzeb/input-remapper/blob/2.2.1/inputremapper/injection/macros/macro.py#L83-L87
So even after the shared variable flips to stop the clicking, the macro
instance keeps occupying that mapping's "running" slot until the fixed
repeat count exhausts — which can be minutes depending on how the count
was sized — silently swallowing every re-press of the trigger key in the
meantime. From the user's perspective: works once, then appears to just
stop responding.
Fix
while_eq(variable, value, macro)re-reads the variable every iteration(like
if_eqdoes) instead of deciding a count up front (likerepeatdoes), so
run()actually returns as soon as the condition no longerholds:
This required a genuinely new task rather than composing existing ones —
none of
repeat/hold/toggle/if_tap/if_singlesupport anexternally-driven, unbounded, promptly-exiting loop;
hold/toggle'searly-exit mechanism (
press_trigger()/release_trigger()) is scoped tothe same trigger that started them, and
repeat's bound is fixed atstart with no way to shorten it once running.
Changes
inputremapper/injection/macros/tasks/while_eq.py— new taskinputremapper/injection/macros/parse.py— registerswhile_eqinTASK_CLASSESreadme/macros.md— docs entry alongsidetoggletests/unit/test_macros/test_while_eq.py— covers: no-op when thecondition starts false, stopping promptly when the variable is changed
by other code mid-loop, and immediate re-triggerability after stopping
Ran the full
tests/unit/test_macrossuite locally (134 tests, allpassing) plus the new file on its own.
Happy to adjust naming/placement/docs wording if you'd prefer something
different —
while_eqwas chosen to mirrorif_eq's naming rather thanintroduce an unrelated verb (I'd originally called it
until, which isbackwards —
untilconventionally means "loop while false", and thisloops while true).