Skip to content

Commit

Permalink
fixed mouse too slow, allowing speeds < 1
Browse files Browse the repository at this point in the history
  • Loading branch information
sezanzeb committed Jan 3, 2025
1 parent a5a13a0 commit 3f1ed50
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 8 deletions.
29 changes: 25 additions & 4 deletions inputremapper/injection/macros/tasks/mouse_xy.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from __future__ import annotations

import asyncio
import time
from typing import Union

from evdev._ecodes import REL_Y, REL_X
Expand All @@ -31,6 +32,25 @@
from inputremapper.injection.macros.task import Task


async def precise_iteration_frequency(rate: float):
"""asyncio.sleep might end up sleeping too long, for whatever reason. Maybe other
async function calls that take longer than expected in the background. This
generator can be used to achieve the proper iteration frequency.
"""
sleep = 1 / rate
corrected_sleep = sleep
error = 0

while True:
start = time.time()

yield

corrected_sleep -= error
await asyncio.sleep(corrected_sleep)
error = (time.time() - start) - sleep


class MouseXYTask(Task):
"""Move the mouse cursor."""

Expand Down Expand Up @@ -79,12 +99,15 @@ async def axis(
if acceleration <= 0:
displacement = int(speed)

while self.is_holding():
async for _ in precise_iteration_frequency(self.mapping.rel_rate):
if not self.is_holding():
return

# Cursors can only move by integers. To get smooth acceleration for
# small acceleration values, the cursor needs to move by a pixel every
# few iterations. This can be achieved by remembering the decimal
# places that were cast away, and using them for the next iteration.
if acceleration and abs(current_speed) < abs(speed):
if acceleration:
current_speed += acceleration
current_speed = direction * min(abs(current_speed), abs(speed))
displacement_accumulator += current_speed
Expand All @@ -93,5 +116,3 @@ async def axis(

if displacement != 0:
callback(EV_REL, code, displacement)

await asyncio.sleep(1 / self.mapping.rel_rate)
4 changes: 2 additions & 2 deletions readme/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Bear in mind that anti-cheat software might detect macros in games.
> mouse(
> direction: up | down | left | right,
> speed: int,
> acceleration: float | None
> acceleration: int | float
> )
> ```
>
Expand All @@ -183,7 +183,7 @@ Bear in mind that anti-cheat software might detect macros in games.
> mouse(
> x: int | float,
> y: int | float,
> acceleration: float | None
> acceleration: int | float
> )
> ```
>
Expand Down
27 changes: 25 additions & 2 deletions tests/unit/test_macros/test_mouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@ async def test_mouse_acceleration(self):
self.result,
)

async def test_rate(self):
# It should move 200 times per second by 1px, for 0.2 seconds.
rel_rate = 200
time = 0.2
speed = 1
expected_movement = time * rel_rate * speed

await self._run_mouse_macro(f"mouse(down, {speed})", time, rel_rate)
total_movement = sum(event[2] for event in self.result)
self.assertAlmostEqual(float(total_movement), expected_movement, delta=1)

async def test_slow_movement(self):
await self._run_mouse_macro(f"mouse(down, 0.1)", 0.2, 200)
total_movement = sum(event[2] for event in self.result)
self.assertAlmostEqual(total_movement, 4, delta=1)

async def test_mouse_xy_acceleration_1(self):
await self._run_mouse_macro("mouse_xy(2, -10, 0.09001)", 0.1)
self.assertEqual(
Expand Down Expand Up @@ -165,11 +181,18 @@ def _get_x_movement(self):
def _get_y_movement(self):
return [event for event in self.result if event[1] == REL_Y]

async def _run_mouse_macro(self, code: str, time: float):
async def _run_mouse_macro(
self,
code: str,
time: float,
rel_rate: int = DummyMapping.rel_rate,
):
dummy_mapping = DummyMapping()
dummy_mapping.rel_rate = rel_rate
macro_1 = Parser.parse(
code,
self.context,
DummyMapping,
dummy_mapping,
)
macro_1.press_trigger()
asyncio.ensure_future(macro_1.run(self.handler))
Expand Down

0 comments on commit 3f1ed50

Please sign in to comment.