autohotkeylong-press

Multiple key press detection with AHK


I am using the following AutoHotkey script originally written by Laszlo to detect single, multiple, short and long presses of the Ctrl key:

Morse(timeout = 150) { ;
    tout := timeout/1000
    key := RegExReplace(A_ThisHotKey,"[\*\~\$\#\+\!\^]")
    Loop {
        t := A_TickCount
        KeyWait %key%
        Pattern .= A_TickCount-t > timeout
        KeyWait %key%,DT%tout%
        If (ErrorLevel)
            Return Pattern
    }
}

~Ctrl::
    p := Morse()
    If (p = "0")
        MsgBox Short press
    Else If (p = "1")
        MsgBox Long press
    Else If (p = "00")
        MsgBox Two short presses
    Else If (p = "01")
        MsgBox Short+Long presses
    Else
        MsgBox Press pattern %p%
    Return

The script works well to differentiate between Short and long Ctrl presses. However, I am facing an issue when using Ctrl key together with another keys like z, c, v, etc. In these cases, the script detects these combos as a long press on Ctrl key and dispalys the "Long press" message box. Instead, I want it to ignore any combination of Ctrl + other keys and allow the default system behavior.

Appreciate any help modifing the script


Solution

  • Here is the correct answer:

    #Requires AutoHotkey v2.0
    #SingleInstance Force
    
    ; Morse Function (To detect single, double, tripple or ... , short or long press)
    Morse(Timeout := 200, PatternSequence := 10) ; Set patternSequence to 3 or even 2 for better performance. However, this will decrease the number of patterns detected.
    {
        Global Pattern := ""
        Win := WinExist("A")
        RegExMatch(Hotkey := A_ThisHotkey, "\W$|\w*$", &Key)
        IsModifier := InStr(Hotkey, "Control") or InStr(Hotkey, "Alt") Or InStr(Hotkey, "Shift") Or InStr(Hotkey, "Win")
        CoordMode("Mouse", "Screen")
        MouseGetPos(&X1, &Y1, &Win1)
        Counter := 0
    
        Loop (PatternSequence) {
            ; Disabling Alt menu acceleration / Windows Start menu
            If InStr(Hotkey, "Alt") Or InStr(Hotkey, "Win") ; Comment second condition if you need WIN key to open the start menu.
                Send("{Blind}{vkE8}")
    
            T := A_TickCount
            ErrorLevel := !KeyWait(Key[0])
            Pattern .= A_TickCount - T > Timeout
            MouseGetPos(&X2, &Y2, &Win2)
            
            ; Skiping the operation for modifiers, if another key is pressed or mouse is moved.
            IF (IsModifier) And (((Win Key[0] Hotkey Win1) != (WinExist("A") A_PriorKey A_ThisHotkey Win2)) Or (30 < (X2 - X1) ** 2 + (Y2 - Y1) ** 2))
                Exit
                
            ; Skiping the last keywait to improve speed!
            Counter += 1
            If (Counter = PatternSequence)
                return Pattern
    
            ; Waiting for the next key sequence to be pressed      
            ErrorLevel := !KeyWait(Key[0], "DT" Timeout / 1500)
            If ErrorLevel
                return Pattern
        }
    }
    
    
    ; Hotkey handler
    ~LControl::
    ~LAlt::
    ~LShift::
    ~LWin::
    XButton1::
    ; Tab::
    ; Capslock::
    {
        hotkey := A_ThisHotkey
        Patterm := Morse() ;  Morse(, 2) ; Call like this if you need the following 6 patterns only.
        Switch Patterm
        {
            Case "0":
                MsgBox(hotkey " " Patterm)
            Case "1":
                MsgBox(hotkey " " Patterm)
            Case "00":
                MsgBox(hotkey " " Patterm)
            Case "01":
                MsgBox(hotkey " " Patterm)
            Case "10":
                MsgBox(hotkey " " Patterm)
            Case "11":
                MsgBox(hotkey " " Patterm)
            Default: ; Preferably comment these two lines after testing
                MsgBox(hotkey " " Patterm)
        }
        Return
    }
    

    CHANGELOG:

    I used the original Morse function written by Laszlo which detects single/multiple, short/long presses of any key. But it had some problems specially with modifier keys.

    With the great help of Rohwedder, the script modified to detect key combinations (eg.Ctrl+z) or mouse movement (eg. Ctrl+Mouse drag) and let them to perform their default functionality.

    At the next step, I improved the script to reach a better performance.

    Finally, I solved the conflicts of the Alt key with disabling "Alt key acceleration", as well as the Win key conflicts with the start menu.

    USAGE:

    For different hotkeys, you can set different timeouts, as well as different pattern sequences.

    For example, in a real case, I needed a single Shift key press (Pattern: 0) to send Alt + Shift to change the input language. But I noticed a lag when switching languages while typing very fast. So, I decided to call the Morse function with these parameters: Pattern := Morse(400, 1).

    This removed the lag, since it goes through the loop just once. It also fixed my other problem which conflicting short and long Shift key presses, as the timeout to detect the long press increased. The downside is that there are only two patterns to use: short and long presses !

    REFERENCES:

    Original Morse function by Laszlo

    Rohwedder code