Jump to content
yazansayed

detect long key press

Recommended Posts

Hello, 

I've been learning Autoit for a few hours , for the sole purpose of detecting when I long press an alphabet key, to send the uppercase value , Instead of having to press SHIFT+key everytime, just like in mobile devices.

here's the code so far:

#include <Misc.au3>
#include <MsgBoxConstants.au3>
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****


;
Global $hDLL = DllOpen("user32.dll")
HotKeySet("q", "skipPress")
HotKeySet("a", "skipPress")
Global $count = 0
Global $letter
Global $upper = False
Global $tapped = False

While 1
    ;;;;;;;;;
    If _IsPressed("10", $hDLL) Or _IsPressed("11", $hDLL) Or _IsPressed("12", $hDLL) Or _IsPressed("5B", $hDLL) Or _IsPressed("5C", $hDLL) Then
        ;skip when ctrl,shift,alt,Lwin,Rwin presses
    ElseIf _IsPressed("51", $hDLL) Then
        $tapped = True
        $letter = "q"
        While _IsPressed("51", $hDLL)
            $count += 1
            Sleep(50)
        WEnd


    ElseIf _IsPressed("41", $hDLL) Then
        $tapped = True
        $letter = "a"
        While _IsPressed("41", $hDLL)
            $count += 1
            Sleep(50)
        WEnd


    Else
        $tapped = False
    EndIf
    ;;;;;;;;

    If $count > 3 Then
        $upper = True
    Else
        $upper = False
    EndIf
    $count = 0
    
    ;;;;;;;;;;
    If $tapped And $upper Then
        Send(StringUpper($letter))
    ElseIf $tapped Then
        HotKeySet($letter)
        Send($letter)
        HotKeySet($letter, "skipPress")
        $tapped = False
    EndIf
    ;;;;;;;;;
    Sleep(50)
WEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func skipPress()

EndFunc   ;==>skipPress

I've just included the letters "a" and "q" in the above code,, the code is working fine and as intended, but i was wondering if there's a more efficient approach than a loop.

thanks a lot.

Share this post


Link to post
Share on other sites
2 hours ago, yazansayed said:

but i was wondering if there's a more efficient approach than a loop.

"more efficient approach than a loop."  doesn't really say anything or does it ..?

Do you maybe need this work around for those keys  only when a specific gui is in-front .etc

Try emphasizing or figuring out at under what circumstances you will need this to work and do, then take it on further ..

Edit : i think you can move everything into 

Func skipPress()

loosing :

While 1
    ;;;;;;;;;
    If _IsPressed("10", $hDLL) Or _IsPressed("11", $hDLL) Or _IsPressed("12", $hDLL) Or _IsPressed("5B", $hDLL) Or _IsPressed("5C", $hDLL) Then

 

    ;;;;;;;;;
    Sleep(50)
WEnd

starting it from If _IsPressed("51", $hDLL) Then

and it will still work for you

Deye

Edited by Deye

Share this post


Link to post
Share on other sites

Streamlined on @Deye's idea :

#include <Misc.au3>
#include <MsgBoxConstants.au3>

Global Const $hDLL = DllOpen("user32.dll")
Global Const $sList = "abcdefghijklmnopqrstuvwxyz"
Global Const $aList = StringSplit($sList,"",$STR_NOCOUNT)
Global $bTreat = False

OnAutoItExitRegister (_Bye)

HotKeySet ("{ESC}", _Exit)
For $c in $aList
  HotKeySet ($c,_Alpha)
Next

While True
  Sleep (100)
WEnd

Func _Alpha()
  If $bTreat Then Return
  $bTreat = True
  Local Const $iStart = Asc ("a"), $iVK = Dec("41"), $char = @HotKeyPressed
  Local $sVK = Hex(Asc($char) - $iStart + $iVK,2), $iCount = 0
  HotKeySet (@HotKeyPressed)
  While _IsPressed($sVK, $hDLL)
    $iCount += 1
    Sleep (50)
  WEnd
  Send ($iCount > 3 ? StringUpper($char) : $char)
  HotKeySet ($char, _Alpha)
  $bTreat = False
EndFunc

Func _Exit ()
  Exit
EndFunc

Func _Bye ()
  DllClose ($hDLL)
EndFunc

That is working for all letters :)

ps.  I copy @hotkeypressed into $char at the beginning to avoid conflict with another hot key.

Edited by Nine

Share this post


Link to post
Share on other sites

Sigh...forgot about VK is in hex and optimized the code a bit.  I truly didn't test it enough.

Corrected the code. Thanks again @Exit

Edit : found another problem, when we type fast it may loose some chars.  Need to think about this approach, might not be the way after all...

Edited by Nine

Share this post


Link to post
Share on other sites

this is such a nice community, thank you all.

17 hours ago, Deye said:

"more efficient approach than a loop."  doesn't really say anything or does it ..?

 

the script i provided creates a loop that runs all the time to check for key presses, so it's not very efficient in cpu terms, i know it won't draw much cpu since it's sleeping most of the time (50 ms), but still it's always looping.

@Nine, thanks for the code, I didn't understand some parts of it, but it gave me a hint of only starting a loop when a key is pressed, instead of an always-running loop.

I wrote this snippet to manage the letter "q", which works fine without skipping any press.

#include <Misc.au3>
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****


Global $hDLL = DllOpen("user32.dll")
HotKeySet("{ESC}", _Exit)
HotKeySet("q", "_loop")
Global $count = 0


While 1
    Sleep(100000)
WEnd

Func _loop()
    While _IsPressed("51", $hDLL)
        $count += 1
        Sleep(50)
    WEnd
    If $count < 5 Then ;send lowercase
        HotKeySet("q")
        Send("q")
        HotKeySet("q", "_loop")
    Else ;send uppercase
        Send("Q")
    EndIf

    $count = 0
EndFunc   ;==>_loop

;;;;;;;;;;;;;;;;;

Func _Exit()
    Exit
EndFunc   ;==>_Exit

the remaining thing is how to write less code to manage all letters, this would've been simple if HotKeySet accepts a function with parameters, not just the function name.
for example: HotKeySet ($key, _loop($key))
thank you again

 

Share this post


Link to post
Share on other sites

Edit: starting a loop after each key press would mess things up, since these loops won't finish in the same order the keys were physically pressed, for the same reason i don't think that any approach that calls a function after each physical key press would be correct, if you type fast the called functions won't finish in the same order the keys were actually pressed, 

here's a code that would work if you type slow but would mess things up if you type fast, it's the same code @Nine posted I guess.

#include <Misc.au3>
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****


Global $hDLL = DllOpen("user32.dll")
HotKeySet("{ESC}", _Exit)
;HotKeySet("a", "_loop")
Global Const $sList = "abcdefghijklmnopqrstuvwxyz"
Global Const $aList = StringSplit($sList,"",$STR_NOCOUNT)

For $c in $aList
  HotKeySet ($c,"_loop")
Next
Global $count = 0


While 1
    Sleep(100000)
WEnd


Func _loop()
Local Const $char = @HotKeyPressed
Local Const $vk = getKey($char)

    While _IsPressed($vk, $hDLL)
        $count += 1
        Sleep(50)
    WEnd
    If $count < 5 Then ;send lowercase
        HotKeySet($char)
        Send($char)
        HotKeySet($char, "_loop")
    Else ;send uppercase
        Send(StringUpper($char))
    EndIf

    $count = 0
EndFunc   ;==>_loop

;;;;;;;;;;;;;;;;;
Func getKey($mChar)
    Local $res = ""
    Switch $mChar
        Case "a"
            $res = "41"
        Case "b"
            $res = "42"
        Case "c"
            $res = "43"
        Case "d"
            $res = "44"
        Case "e"
            $res = "45"
        Case "f"
            $res = "46"
        Case "g"
            $res = "47"
        Case "h"
            $res = "48"
        Case "i"
            $res = "49"
        Case "j"
            $res = "4A"
        Case "k"
            $res = "4B"
        Case "l"
            $res = "4C"
        Case "m"
            $res = "4D"
        Case "n"
            $res = "4E"
        Case "o"
            $res = "4F"
        Case "p"
            $res = "50"
        Case "q"
            $res = "51"
        Case "r"
            $res = "52"
        Case "s"
            $res = "53"
        Case "t"
            $res = "54"
        Case "u"
            $res = "55"
        Case "v"
            $res = "56"
        Case "w"
            $res = "57"
        Case "x"
            $res = "58"
        Case "y"
            $res = "59"
        Case "z"
            $res = "5A"

        Case Else
            $res = ""
    EndSwitch
    Return $res
EndFunc   ;==>getKey

Func _Exit()
    Exit
EndFunc   ;==>_Exit

 

Share this post


Link to post
Share on other sites

okay, things keep getting worse, I went back to the code in the first post, the one with always-running loop, and included all other letter to the if-else chain, and again, if i type slow things work fine, and if i type fast things get messy, so now I don't know any approach at all,efficient or not.

 if anyone wants to test it, here's the code:

#include <Misc.au3>
#include <MsgBoxConstants.au3>
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****


;
HotKeySet("{ESC}", _Exit)

Global $hDLL = DllOpen("user32.dll")
HotKeySet("q", "skipPress")
HotKeySet("a", "skipPress")

Global $count = 0
Global $letter
Global $shouldSendUpperCase = False
Global $didPressAkey = False

While 1

    ;;;;;;;;;
    If _IsPressed("10", $hDLL) Or _IsPressed("11", $hDLL) Or _IsPressed("12", $hDLL) Or _IsPressed("5B", $hDLL) Or _IsPressed("5C", $hDLL) Then
        ;skip when ctrl,shift,alt,Lwin,Rwin presses

        ;;;;;;;;;;;;;;;;;;;start of elseif
    ElseIf _IsPressed("41", $hDLL) Then
        $didPressAkey = True
        $letter = "a"
        While _IsPressed("41", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("42", $hDLL) Then
        $didPressAkey = True
        $letter = "b"
        While _IsPressed("42", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("43", $hDLL) Then
        $didPressAkey = True
        $letter = "c"
        While _IsPressed("43", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("44", $hDLL) Then
        $didPressAkey = True
        $letter = "d"
        While _IsPressed("44", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("45", $hDLL) Then
        $didPressAkey = True
        $letter = "e"
        While _IsPressed("45", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("46", $hDLL) Then
        $didPressAkey = True
        $letter = "f"
        While _IsPressed("46", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("47", $hDLL) Then
        $didPressAkey = True
        $letter = "g"
        While _IsPressed("47", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("48", $hDLL) Then
        $didPressAkey = True
        $letter = "h"
        While _IsPressed("48", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("49", $hDLL) Then
        $didPressAkey = True
        $letter = "i"
        While _IsPressed("49", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("4A", $hDLL) Then
        $didPressAkey = True
        $letter = "j"
        While _IsPressed("4A", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("4B", $hDLL) Then
        $didPressAkey = True
        $letter = "k"
        While _IsPressed("4B", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("4C", $hDLL) Then
        $didPressAkey = True
        $letter = "l"
        While _IsPressed("4C", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("4D", $hDLL) Then
        $didPressAkey = True
        $letter = "m"
        While _IsPressed("4D", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("4E", $hDLL) Then
        $didPressAkey = True
        $letter = "n"
        While _IsPressed("4E", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("4F", $hDLL) Then
        $didPressAkey = True
        $letter = "o"
        While _IsPressed("4F", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("50", $hDLL) Then
        $didPressAkey = True
        $letter = "p"
        While _IsPressed("50", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("51", $hDLL) Then
        $didPressAkey = True
        $letter = "q"
        While _IsPressed("51", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("52", $hDLL) Then
        $didPressAkey = True
        $letter = "r"
        While _IsPressed("52", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("53", $hDLL) Then
        $didPressAkey = True
        $letter = "s"
        While _IsPressed("53", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("54", $hDLL) Then
        $didPressAkey = True
        $letter = "t"
        While _IsPressed("54", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("55", $hDLL) Then
        $didPressAkey = True
        $letter = "u"
        While _IsPressed("55", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("56", $hDLL) Then
        $didPressAkey = True
        $letter = "v"
        While _IsPressed("56", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("57", $hDLL) Then
        $didPressAkey = True
        $letter = "w"
        While _IsPressed("57", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("58", $hDLL) Then
        $didPressAkey = True
        $letter = "x"
        While _IsPressed("58", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("59", $hDLL) Then
        $didPressAkey = True
        $letter = "y"
        While _IsPressed("59", $hDLL)
            $count += 1
            Sleep(50)
        WEnd
    ElseIf _IsPressed("5A", $hDLL) Then
        $didPressAkey = True
        $letter = "z"
        While _IsPressed("5A", $hDLL)
            $count += 1
            Sleep(50)
        WEnd

        ;;;;;;;;;;;;;end of elseif

    Else
        $didPressAkey = False
    EndIf
    ;;;;;;;;

    If $count > 3 Then
        $shouldSendUpperCase = True
    Else
        $shouldSendUpperCase = False
    EndIf
    $count = 0

    ;;;;;;;;;;
    If $didPressAkey And $shouldSendUpperCase Then
        Send(StringUpper($letter))
    ElseIf $didPressAkey Then
        HotKeySet($letter)
        Send($letter)
        HotKeySet($letter, "skipPress")

    EndIf
    $didPressAkey = False
    ;;;;;;;;;
    Sleep(50)
WEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func skipPress()

EndFunc   ;==>skipPress

Func _Exit()
    Exit
EndFunc   ;==>_Exit

 

Share this post


Link to post
Share on other sites

sorry i'm posting alot of edits, i guess new users are not allowed to edit their posts,, in the last code I forgot to do HotKeySet("a", "skipPress")  for all letters, but still things are messy when i type fast.

::UPDATE

found a better approach by using Timers.au3 functions, but it needs some fixing.

#include <Misc.au3>
#include <Timers.au3>

;~ #include <MsgBoxConstants.au3>
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****


HotKeySet("{ESC}", _Exit)
Global $hDLL = DllOpen("user32.dll")
Global Const $sList = "abcdefghijklmnopqrstuvwxyz"
Global Const $aList = StringSplit($sList, "", $STR_NOCOUNT)
Global $interval_timer, $counter

For $c In $aList
    HotKeySet($c, "_fun1")
Next

While 1
    Sleep(100000)
WEnd

Func _fun1()
    Local $itv_timer
    $itv_timer = _Timer_Diff($interval_timer)
    $interval_timer = _Timer_Init()

    If $counter >= 20 Then
        $counter = 0
        Send(StringUpper(@HotKeyPressed))
    ElseIf $itv_timer < 50 Then
        $counter = $counter + 1
    Else

        HotKeySet(@HotKeyPressed)
        Send(@HotKeyPressed)
        HotKeySet(@HotKeyPressed, "_fun1")
        $counter = 0

    EndIf
    $interval_timer = _Timer_Init()
EndFunc   ;==>_fun1


Func _Exit()
    Exit
EndFunc   ;==>_Exit

 

test:

-when fast-typing "hello world" it types hello world correctly

-when typing "Hello World" it types hhHello wwWorld

so i guess it just needs fixing the logic in the if-else chain.

Edited by yazansayed

Share this post


Link to post
Share on other sites

My best effort.  I have spent way too much time on this :lol:.  But it was fun.  It is not perfect, but maybe with some tweeks...  

#include <WinAPIConv.au3>
#include <WinAPISys.au3>
#include <WindowsConstants.au3>
#include <Misc.au3>

Global Const $iVKStart = 0x41, $iIDBase = 0x0100
OnAutoItExitRegister('OnAutoItExit')

Local $hWnd = GUICreate("")
GUIRegisterMsg($WM_HOTKEY, 'WM_HOTKEY')
HotKeySet("{ESC}", _Exit)

For $i = $iVKStart To $iVKStart + 25
  _WinAPI_RegisterHotKey($hWnd, $iIDBase + $i, 0, $i)
Next

While 1
  Sleep(100)
WEnd

Func WM_HOTKEY($hWnd, $iMsg, $wParam, $lParam)
  #forceref $hWnd, $iMsg, $wParam
  Local $iCount = 0, $iVK = _WinAPI_HiWord($lParam), $sVK = Hex($iVK, 2), $iFirst = Asc("a")
  Local $char = Chr($iVK - $iVKStart + $iFirst)
  While _IsPressed($sVK)
    $iCount += 1
  WEnd
  If $iCount > 15000 Then
    Send(StringUpper($char))
  Else
    _WinAPI_UnregisterHotKey($hWnd, $iIDBase + $iVK)
    Send($char)
    _WinAPI_RegisterHotKey($hWnd, $iIDBase + $iVK, 0, $iVK)
  EndIf
EndFunc   ;==>WM_HOTKEY

Func OnAutoItExit()
  For $i = $iVKStart To $iVKStart + 25
    _WinAPI_UnregisterHotKey($hWnd, $iIDBase + $i)
  Next
EndFunc   ;==>OnAutoItExit

Func _Exit()
  Exit
EndFunc   ;==>_Exit

 

Share this post


Link to post
Share on other sites
8 hours ago, Nine said:

My best effort.  I have spent way too much time on this :lol:.  But it was fun.  It is not perfect, but maybe with some tweeks...  

thanks a lot, it's always fun to solve problems, be safe 🌹

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...