@@ -1012,44 +1012,63 @@ func (s *ApiService) doDragMouseSmooth(ctx context.Context, log *slog.Logger, bo
10121012
10131013 result := mousetrajectory .GenerateMultiSegmentTrajectory (waypoints , screenWidth , screenHeight , body .DurationMs )
10141014 points := result .Points
1015- stepDelayMs := result .StepDelayMs
1015+ baseDelayMs := result .StepDelayMs
10161016
10171017 if len (points ) < 2 {
10181018 return nil
10191019 }
10201020
1021- for i := 1 ; i < len (points ); i ++ {
1022- select {
1023- case <- ctx .Done ():
1024- return & executionError {msg : "drag movement cancelled" }
1025- default :
1026- }
1021+ numSteps := len (points ) - 1
10271022
1023+ // Build a single xdotool arg slice with inline sleep directives.
1024+ // Use smoothstep easing: slow at start (pickup) and end (placement),
1025+ // fast in the middle, matching natural human drag behavior.
1026+ args := []string {}
1027+ for i := 1 ; i <= numSteps ; i ++ {
10281028 dx := points [i ][0 ] - points [i - 1 ][0 ]
10291029 dy := points [i ][1 ] - points [i - 1 ][1 ]
1030- if dx != 0 || dy != 0 {
1031- args := []string {"mousemove_relative" , "--" , strconv .Itoa (dx ), strconv .Itoa (dy )}
1032- if output , err := defaultXdoTool .Run (ctx , args ... ); err != nil {
1033- log .Error ("xdotool mousemove_relative failed" , "err" , err , "output" , string (output ), "step" , i )
1034- return & executionError {msg : "failed during smooth drag movement" }
1035- }
1030+ if dx == 0 && dy == 0 {
1031+ continue
10361032 }
1037- jitter := stepDelayMs
1038- if stepDelayMs > 3 {
1039- jitter = stepDelayMs + rand .Intn (5 ) - 2
1033+ args = append (args , "mousemove_relative" , "--" , strconv .Itoa (dx ), strconv .Itoa (dy ))
1034+
1035+ if i < numSteps {
1036+ delay := smoothStepDelay (i , numSteps , baseDelayMs * 2 , baseDelayMs / 2 )
1037+ jitter := delay + rand .Intn (5 ) - 2
10401038 if jitter < 3 {
10411039 jitter = 3
10421040 }
1041+ args = append (args , "sleep" , fmt .Sprintf ("%.3f" , float64 (jitter )/ 1000.0 ))
10431042 }
1044- if err := sleepWithContext (ctx , time .Duration (jitter )* time .Millisecond ); err != nil {
1045- return & executionError {msg : "drag movement cancelled" }
1043+ }
1044+
1045+ if len (args ) > 0 {
1046+ log .Info ("executing xdotool (smooth drag move)" , "steps" , numSteps , "segments" , len (body .Path )- 1 )
1047+ if output , err := defaultXdoTool .Run (ctx , args ... ); err != nil {
1048+ log .Error ("xdotool smooth drag move failed" , "err" , err , "output" , string (output ))
1049+ return & executionError {msg : "failed during smooth drag movement" }
10461050 }
10471051 }
10481052
10491053 log .Info ("executed smooth drag movement" , "points" , len (points ), "segments" , len (body .Path )- 1 )
10501054 return nil
10511055}
10521056
1057+ // smoothStepDelay maps position i/n through a smoothstep curve to produce
1058+ // a delay in [fastMs, slowMs]. Slow at start and end, fast in the middle.
1059+ // smoothstep(t) = 3t² - 2t³
1060+ func smoothStepDelay (i , n , slowMs , fastMs int ) int {
1061+ if n <= 1 {
1062+ return slowMs
1063+ }
1064+ t := float64 (i ) / float64 (n )
1065+ // Remap t so that 0 and 1 map to 1 (slow) and 0.5 maps to 0 (fast).
1066+ // Use distance from center: d = |2t - 1|, then smoothstep on d.
1067+ d := math .Abs (2 * t - 1 )
1068+ s := d * d * (3 - 2 * d ) // smoothstep
1069+ return fastMs + int (float64 (slowMs - fastMs )* s )
1070+ }
1071+
10531072func (s * ApiService ) DragMouse (ctx context.Context , request oapi.DragMouseRequestObject ) (oapi.DragMouseResponseObject , error ) {
10541073 s .inputMu .Lock ()
10551074 defer s .inputMu .Unlock ()
0 commit comments