Skip to content

Conversation

@ulziibay-kernel
Copy link
Contributor

@ulziibay-kernel ulziibay-kernel commented Feb 11, 2026

  • Add mousetrajectory lib with Bernstein Bezier curves, jitter, easeOutQuad
  • MoveMouse supports smooth=true for curved path, smooth=false for instant
  • OpenAPI: smooth, steps (5-80), step_delay_ms (3-30) on MoveMouseRequest
  • Add cursor-trail demo (before/after instant vs Bezier)

Checklist

  • A link to a related issue in our repository
  • A description of the changes proposed in the pull request.
  • @mentions of the person or team responsible for reviewing proposed changes.

Note

Medium Risk
Changes input simulation behavior and adds timing/cancellation logic around xdotool, which could impact automation reliability (e.g., stuck modifiers, differing cursor paths) and is sensitive to environment-specific cursor state.

Overview
MoveMouse now supports smooth cursor movement (default) using a new Bezier-based trajectory generator, falling back to instant xdotool mousemove when smooth=false or trajectory generation is too short.

The new smooth path uses the current cursor location, issues per-step xdotool mousemove_relative events with small randomized timing jitter, and ensures held modifier keys are released even if the request context is cancelled. The OpenAPI/SDK model is updated with smooth and duration_sec (0.05–5s) parameters, and a new server/lib/mousetrajectory package (with tests) is introduced to generate the humanized point sequences.

Written by Cursor Bugbot for commit 6397a13. This will update automatically on new commits. Configure here.

- Add mousetrajectory lib with Bernstein Bezier curves, jitter, easeOutQuad
- MoveMouse supports smooth=true for curved path, smooth=false for instant
- OpenAPI: smooth, steps (5-80), step_delay_ms (3-30) on MoveMouseRequest
- Add cursor-trail demo (before/after instant vs Bezier)

Co-authored-by: Cursor <[email protected]>
@ulziibay-kernel ulziibay-kernel force-pushed the ulziibay-kernel/add-human-like-curves branch from c1534a3 to 1c0a302 Compare February 11, 2026 17:25
Copy link
Contributor

@rgarcia rgarcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't compile (see build)

ulziibay-kernel and others added 7 commits February 11, 2026 12:23
- Fix demo README: kernel-images or kernel-images-private (not just private)
- Use await browser.close() for clean demo script shutdown
- Add named constants in mousetrajectory (boundsPadding, knotsCount, etc.)
- Avoid mutating opts.MaxPoints; use local var instead
- Add ctx.Done() check and sleepWithContext in Bezier step loop for cancellation
- Clarify OpenAPI: steps/step_delay_ms ignored when smooth=false
- Add TestHumanizeMouseTrajectory_ZeroLengthPath edge case

Co-authored-by: Cursor <[email protected]>
Demo not needed in this repo.

Co-authored-by: Cursor <[email protected]>
Align with Camoufox humanize:minTime/maxTime. Steps and delay auto-computed from path length and target duration.

Co-authored-by: Cursor <[email protected]>
The deferred modifier key release was using the request context, which
fails if cancelled mid-movement, leaving keys stuck. Use
context.Background() to match the established cleanup pattern.

Co-authored-by: Cursor <[email protected]>
- Eliminate duplicated validation by having MoveMouse call doMoveMouse
  directly instead of reimplementing resolution/bounds checks
- Remove unnecessary moveMouseSmooth/moveMouseInstant wrapper functions
- Compute trajectory MaxPoints from duration_sec so the requested
  duration is actually achievable (remove 30ms step delay upper clamp)
- Skip zero-delta mousemove_relative steps to avoid no-op xdotool calls
- Always pass -- to mousemove_relative for robustness with negative args
- Remove redundant nil check in deferred HoldKeys cleanup
- Clean up duration_sec OpenAPI description (remove internal references)
- Rename defaultMaxTime/defaultMinTime to defaultMaxPoints/defaultMinPoints
- Export MinPoints/MaxPoints constants from mousetrajectory package
- Add clamping tests for MaxPoints below min and above max

Co-authored-by: Cursor <[email protected]>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

dy := points[i][1] - points[i-1][1]
if dx == 0 && dy == 0 {
continue
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipped zero-delta steps break requested movement duration

Medium Severity

When consecutive trajectory points round to the same integer coordinates (common for short-distance moves or high point counts), the continue on dx == 0 && dy == 0 skips both the xdotool call and the sleepWithContext. The stepDelayMs was computed assuming all numSteps steps would execute a sleep, so skipping sleeps causes the actual movement duration to be significantly shorter than the requested DurationSec. For a 20-pixel move with DurationSec=2.0, the many duplicate integer points mean only a fraction of steps actually sleep, producing a sub-second movement instead of the intended 2 seconds.

Additional Locations (1)

Fix in Cursor Fix in Web

}
if output, err := defaultXdoTool.Run(ctx, args...); err != nil {
log.Error("xdotool keydown failed", "err", err, "output", string(output))
return &executionError{msg: "failed to hold modifier keys"}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty HoldKeys array fails in smooth mode

Low Severity

When hold_keys is an empty array (non-nil pointer to []string{}), doMoveMouseSmooth enters the body.HoldKeys != nil block but appends no keydown args, then calls defaultXdoTool.Run(ctx) with zero arguments. This runs bare xdotool which prints help and exits with an error, causing a 500 "failed to hold modifier keys" response. doMoveMouseInstant handles this correctly since the empty loop just adds nothing to the shared args slice, which already contains mousemove x y.

Fix in Cursor Fix in Web

if dx == 0 && dy == 0 {
continue
}
args := []string{"mousemove_relative", "--", strconv.Itoa(dx), strconv.Itoa(dy)}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relative moves drift when Bezier goes off-screen

Medium Severity

The trajectory's boundsPadding = 80 places Bezier control knots up to 80 pixels beyond the start/end bounding box, so intermediate curve points can have negative coordinates when the mouse starts near a screen edge. Since doMoveMouseSmooth uses mousemove_relative, and the X11 server clamps the cursor at screen boundaries, the accumulated relative deltas no longer sum to the correct total displacement. The cursor ends up at the wrong final position, potentially missing subsequent click targets.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants