Multiple key press detection with AHK

265 Views Asked by At

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

1

There are 1 best solutions below

0
Afshin Davoudy On BEST ANSWER

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