2
votes

I wonder if there's something wrong with my code. I try to write a toggle code using F2 to spam ctrl. It does the spam job fairly well but when I press F2 again to toggle it off, it only works occasionally. Wonder if the spamming interfere with my F2 key that would throw it off. Below is the code. Also, could I get a modifier so that the code would run on a separate window on the background?

F2::
Toggle := !Toggle
loop
{ If not Toggle
    { 
    Send, {LCtrl Up}   
      break
    }
   Send, {LCtrl Down}
   sleep, 200
   Send, {LCtrl Up}  
}
Return
3

3 Answers

1
votes

Another approach:

; autoexecute-section (top of the script):
loop_enabled := false ; the loop is disabled by default

#MaxThreadsPerHotkey 2 ; allows us to invoke F2 even if the previous invocation of F2 hasn't completed.

; Press F2 down to enable the loop (the $ symbol facilitates the "P" mode of GetKeyState below).
$F2:: loop_enabled := true

#If (loop_enabled) ; If you enable the loop by pressing F2 down

    $F2 Up::  ; release F2 to start the loop
        ToolTip, Looping
        Loop
        {
            Send, {LCtrl Down}
            Loop 20
            {
                If !GetKeyState("F2", "P") 
                    sleep, 10
            }
            Send, {LCtrl Up}
            If GetKeyState("F2", "P") ; by pressing F2 while the loop is enabled
            {
              Send, {Blind}{LCtrl Up}
              ToolTip
              loop_enabled := false      ; disable and
                    break                ; terminate the loop
            }
        }
    Return

#If
0
votes

Your script is Ok. This happens because the loop takes the control of the process and at those moments the hotkey is not responsive - so it is by design so.
For this application I would write the whole app as a loop. This is a bit less compact but it should be robust and makes clear what exactly happens.

Below is a working example.
To adjust the speed of repetition - change T_press and T_release constants.
Also you can change the sleep 10 to increase/decrease the speed of the loop.

Toggle := 0
ticks := 0          ; 'clock' counter

R_prev := getkeystate("F2")

T_press := 4            ; ticks before  Ctrl press
T_release := 40     ; ticks before Ctrl release  

tooltip,  Paused

loop
{ 
    sleep 10            ; loop interval
    R := getkeystate("F2") 
    RX := (R > R_prev)
    R_prev := R
    if RX {
        if Toggle {
            Toggle := 0
            ticks := 0
            send {LCtrl up}  
            tooltip,  Paused
        } else {
            Toggle := 1
        }
    }

    if Toggle {
        ticks += 1
        if (ticks = T_press) {
            send {LCtrl down}
            ; tooltip,  Pressed -- %ticks%
        }
        if (ticks = T_release) {
            send {LCtrl up}  
            ; tooltip,  Released -- %ticks%     
            ticks := 0
        }
        tooltip,  ctrl_on: %pressed% --- %ticks%
    }
}
0
votes

Another good approach is to use Timers.

Timers turn out to be interruptable so there should be no issues with stopping them immediately. For your task - the trick is to set two timers with the same period and always start them with an offset. (see the sleep 120 line). This and the period can be adjusted to your needs.

settimer, fire_stop, 400
settimer, fire, 400
settimer, fire, off
settimer, fire_stop, off

Toggle := 0

F2::
    Toggle := !Toggle
    if Toggle {
        settimer, fire_stop, on
        sleep 120                   ; offset between timers
        settimer, fire, on
        gosub fire
    } else {
        settimer, fire, off
        settimer, fire_stop, off
        gosub fire_stop
    }
return

fire:
    tooltip  "key is down"  
    send {LCtrl down}
return

fire_stop:
    tooltip  "key is up" 
    send {LCtrl up}
return