mirror of
https://github.com/sezanzeb/input-remapper
synced 2024-11-10 01:13:29 +00:00
Add optional acceleration to the mouse macro. (#931)
* Add optional acceleration to the mouse macro. * Make accelerated mouse motion more accurate by accumulating fractional component of the speed. * Apply suggestions from code review Co-authored-by: Tobi <28510156+sezanzeb@users.noreply.github.com> --------- Co-authored-by: Tobi <28510156+sezanzeb@users.noreply.github.com>
This commit is contained in:
parent
eca6fde655
commit
f06b3ed601
@ -469,10 +469,16 @@ class Macro:
|
||||
self.tasks.append(lambda handler: handler(type_, code, value))
|
||||
self.tasks.append(self._keycode_pause)
|
||||
|
||||
def add_mouse(self, direction: str, speed: int):
|
||||
def add_mouse(
|
||||
self,
|
||||
direction: str,
|
||||
speed: int,
|
||||
acceleration: Optional[float] = None,
|
||||
):
|
||||
"""Move the mouse cursor."""
|
||||
_type_check(direction, [str], "mouse", 1)
|
||||
speed = _type_check(speed, [int], "mouse", 2)
|
||||
acceleration = _type_check(acceleration, [float, None], "mouse", 3)
|
||||
|
||||
code, value = {
|
||||
"up": (REL_Y, -1),
|
||||
@ -482,9 +488,29 @@ class Macro:
|
||||
}[direction.lower()]
|
||||
|
||||
async def task(handler: Callable):
|
||||
resolved_speed = value * _resolve(speed, [int])
|
||||
resolved_speed = _resolve(speed, [int])
|
||||
resolved_accel = _resolve(acceleration, [float, None])
|
||||
|
||||
if resolved_accel:
|
||||
current_speed = 0.0
|
||||
displacement_accumulator = 0.0
|
||||
displacement = 0
|
||||
else:
|
||||
displacement = resolved_speed
|
||||
|
||||
while self.is_holding():
|
||||
handler(EV_REL, code, resolved_speed)
|
||||
# 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 resolved_accel and current_speed < resolved_speed:
|
||||
current_speed += resolved_accel
|
||||
current_speed = min(current_speed, resolved_speed)
|
||||
displacement_accumulator += current_speed
|
||||
displacement = int(displacement_accumulator)
|
||||
displacement_accumulator -= displacement
|
||||
|
||||
handler(EV_REL, code, value * displacement)
|
||||
await asyncio.sleep(1 / self.mapping.rel_rate)
|
||||
|
||||
self.tasks.append(task)
|
||||
|
@ -124,9 +124,11 @@ Bear in mind that anti-cheat software might detect macros in games.
|
||||
### mouse
|
||||
|
||||
> Moves the mouse cursor
|
||||
>
|
||||
> If `acceleration` is provided then the cursor will accelerate from zero to a maximum speed of `speed`.
|
||||
>
|
||||
> ```c#
|
||||
> mouse(direction: str, speed: int)
|
||||
> mouse(direction: str, speed: int, acceleration: float | None)
|
||||
> ```
|
||||
>
|
||||
> Examples:
|
||||
@ -134,6 +136,7 @@ Bear in mind that anti-cheat software might detect macros in games.
|
||||
> ```c#
|
||||
> mouse(up, 1)
|
||||
> mouse(left, 2)
|
||||
> mouse(down, 10, 0.3)
|
||||
> ```
|
||||
|
||||
### wheel
|
||||
|
@ -1012,6 +1012,20 @@ class TestMacros(MacroTestBase):
|
||||
actual_wheel_hi_res_event_count, expected_wheel_hi_res_event_count * 1.1
|
||||
)
|
||||
|
||||
async def test_mouse_accel(self):
|
||||
macro_1 = parse("mouse(up, 10, 0.9)", self.context, DummyMapping)
|
||||
macro_1.press_trigger()
|
||||
asyncio.ensure_future(macro_1.run(self.handler))
|
||||
|
||||
sleep = 0.1
|
||||
await asyncio.sleep(sleep)
|
||||
self.assertTrue(macro_1.is_holding())
|
||||
macro_1.release_trigger()
|
||||
self.assertEqual(
|
||||
[(2, 1, 0), (2, 1, -2), (2, 1, -3), (2, 1, -4), (2, 1, -4), (2, 1, -5)],
|
||||
self.result,
|
||||
)
|
||||
|
||||
async def test_event_1(self):
|
||||
macro = parse("e(EV_KEY, KEY_A, 1)", self.context, DummyMapping)
|
||||
a_code = system_mapping.get("a")
|
||||
|
Loading…
Reference in New Issue
Block a user