Trong Posted October 5 Posted October 5 (edited) UDF: expandcollapse popup#include-once #include <Array.au3> #include <Misc.au3> ; #INDEX# ======================================================================================================================= ; Title .........: Enhanced Hotkey UDF ; AutoIt Version : 3.3.16.1+ ; Description ...: non-blocking hotkey system ; Author(s) .....: Dao Van Trong - TRONG.PRO ; Version .......: 1.0 ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _HotkeySet ; _HotkeyCheck ; _HotkeyRemove ; _HotkeyClearAll ; _HotkeyGetRegistered ; _HotkeySetDebugMode ; _HotkeyGetStats ; _HotkeySetMultiPressWindow ; =============================================================================================================================== ; #CONSTANTS# =================================================================================================================== Global Const $HOTKEY_FLAG_REPEAT = 1 Global Const $HOTKEY_FLAG_RELEASE = 2 Global Const $HOTKEY_FLAG_HOLD = 4 Global Const $HOTKEY_MOD_CTRL = 1 Global Const $HOTKEY_MOD_ALT = 2 Global Const $HOTKEY_MOD_SHIFT = 4 Global Const $HOTKEY_MOD_WIN = 8 ; =============================================================================================================================== ; #VARIABLES# =================================================================================================================== Global $g_aHotkey_Map[0][6] ; [HashKey, Hotkey, Function, Flags, LastTrigger, HoldStart] Global $g_aHotkey_TrackedVKs[0] ; Only VK codes being monitored Global $g_aHotkey_VKStates[256] ; Current key states Global $g_aHotkey_VKPressTimes[256] ; Key press timestamps Global $g_aHotkey_ModStates[4] ; [Ctrl, Alt, Shift, Win] Global $g_aHotkey_HexCache[256] ; Pre-computed hex strings Global $g_aHotkey_PressHistory[0][11] ; [VK][0-10 timestamps] Global $g_iHotkey_LastCleanup = 0 Global $g_iHotkey_CleanupInterval = 1000 Global $g_iHotkey_HoldThreshold = 500 Global $g_iHotkey_RepeatDelay = 100 Global $g_iHotkey_MultiPressWindow = 800 ; Increased for better usability Global $g_bHotkey_DebugMode = False Global $g_iHotkey_CheckCount = 0 Global $g_iHotkey_TriggerCount = 0 ; =============================================================================================================================== ; #INITIALIZATION# ============================================================================================================== For $i = 0 To 255 $g_aHotkey_VKStates[$i] = False $g_aHotkey_VKPressTimes[$i] = 0 $g_aHotkey_HexCache[$i] = Hex($i, 2) Next For $i = 0 To 3 $g_aHotkey_ModStates[$i] = False Next ; =============================================================================================================================== ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeySet ; Description ...: Registers a non-blocking hotkey ; Syntax ........: _HotkeySet($sHotkey, $sFunction[, $iFlags = 0[, $iMultiPress = 1]]) ; Parameters ....: $sHotkey - Hotkey string (e.g., "^a", "!{F4}", "{SPACE}") ; $sFunction - Function name to call when triggered ; $iFlags - [optional] Combination of HOTKEY_FLAG_* constants (default: 0) ; $iMultiPress - [optional] Number of press-release cycles required (default: 1) ; Return values .: Success - True ; Failure - False, @error set: ; |1 - Invalid hotkey format ; |2 - Invalid parameters ; |3 - Invalid virtual key code ; =============================================================================================================================== Func _HotkeySet($sHotkey, $sFunction, $iFlags = 0, $iMultiPress = 1) If Not IsString($sHotkey) Or $sHotkey = "" Then Return SetError(1, 0, False) If Not IsString($sFunction) Or $sFunction = "" Then Return SetError(2, 0, False) If $iMultiPress < 1 Or $iMultiPress > 10 Then Return SetError(2, 0, False) Local $iModifiers = __Hotkey_ParseModifiers($sHotkey) Local $sKey = __Hotkey_StripModifiers($sHotkey) Local $iVK = __Hotkey_GetVirtualKeyCode($sKey) If $iVK = 0 Then Return SetError(3, 0, False) Local $sHashKey = __Hotkey_GetHashKey($iVK, $iModifiers, $iMultiPress) If _ArraySearch($g_aHotkey_TrackedVKs, $iVK) = -1 Then _ArrayAdd($g_aHotkey_TrackedVKs, $iVK) __Hotkey_InitPressHistory($iVK) EndIf Local $iIndex = __Hotkey_FindInMap($sHashKey) If $iIndex >= 0 Then $g_aHotkey_Map[$iIndex][1] = $sHotkey $g_aHotkey_Map[$iIndex][2] = $sFunction $g_aHotkey_Map[$iIndex][3] = $iFlags $g_aHotkey_Map[$iIndex][4] = TimerInit() $g_aHotkey_Map[$iIndex][5] = 0 Else ReDim $g_aHotkey_Map[UBound($g_aHotkey_Map) + 1][6] Local $iNewIndex = UBound($g_aHotkey_Map) - 1 $g_aHotkey_Map[$iNewIndex][0] = $sHashKey $g_aHotkey_Map[$iNewIndex][1] = $sHotkey $g_aHotkey_Map[$iNewIndex][2] = $sFunction $g_aHotkey_Map[$iNewIndex][3] = $iFlags $g_aHotkey_Map[$iNewIndex][4] = TimerInit() $g_aHotkey_Map[$iNewIndex][5] = 0 EndIf If $g_bHotkey_DebugMode Then Local $sMulti = ($iMultiPress > 1) ? " (" & $iMultiPress & "x)" : "" ConsoleWrite("[HotkeyUDF] Registered: " & $sHotkey & $sMulti & " -> " & $sFunction & " (VK=0x" & $g_aHotkey_HexCache[$iVK] & ")" & @CRLF) EndIf Return True EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeyCheck ; Description ...: Checks and triggers registered hotkeys (call in main loop) ; Syntax ........: _HotkeyCheck() ; Return values .: Number of hotkeys triggered ; =============================================================================================================================== Func _HotkeyCheck() If UBound($g_aHotkey_TrackedVKs) = 0 Then Return 0 $g_iHotkey_CheckCount += 1 Local $iTriggered = 0 Local $iNow = TimerInit() $g_aHotkey_ModStates[0] = _IsPressed("11") $g_aHotkey_ModStates[1] = _IsPressed("12") $g_aHotkey_ModStates[2] = _IsPressed("10") $g_aHotkey_ModStates[3] = _IsPressed("5B") For $i = 0 To UBound($g_aHotkey_TrackedVKs) - 1 Local $iVK = $g_aHotkey_TrackedVKs[$i] Local $bNowPressed = _IsPressed($g_aHotkey_HexCache[$iVK]) Local $bWasPressed = $g_aHotkey_VKStates[$iVK] Local $bJustPressed = $bNowPressed And Not $bWasPressed Local $bJustReleased = Not $bNowPressed And $bWasPressed If $bJustPressed Then $g_aHotkey_VKPressTimes[$iVK] = $iNow EndIf If $bJustReleased Then __Hotkey_RecordPressRelease($iVK, $iNow) EndIf $g_aHotkey_VKStates[$iVK] = $bNowPressed $iTriggered += __Hotkey_CheckTriggersForVK($iVK, $bNowPressed, $bJustPressed, $bJustReleased, $iNow) Next If TimerDiff($g_iHotkey_LastCleanup) > $g_iHotkey_CleanupInterval Then __Hotkey_CleanupHistory($iNow) $g_iHotkey_LastCleanup = $iNow EndIf Return $iTriggered EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeyRemove ; Description ...: Removes a registered hotkey ; =============================================================================================================================== Func _HotkeyRemove($sHotkey, $iMultiPress = 1) Local $iModifiers = __Hotkey_ParseModifiers($sHotkey) Local $sKey = __Hotkey_StripModifiers($sHotkey) Local $iVK = __Hotkey_GetVirtualKeyCode($sKey) If $iVK = 0 Then Return False Local $sHashKey = __Hotkey_GetHashKey($iVK, $iModifiers, $iMultiPress) Return __Hotkey_RemoveFromMap($sHashKey) EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeyClearAll ; Description ...: Clears all registered hotkeys ; =============================================================================================================================== Func _HotkeyClearAll() Local $iCount = UBound($g_aHotkey_Map) ReDim $g_aHotkey_Map[0][6] ReDim $g_aHotkey_TrackedVKs[0] ReDim $g_aHotkey_PressHistory[0][11] For $i = 0 To 255 $g_aHotkey_VKStates[$i] = False $g_aHotkey_VKPressTimes[$i] = 0 Next If $g_bHotkey_DebugMode Then ConsoleWrite("[HotkeyUDF] Cleared all hotkeys (" & $iCount & " removed)" & @CRLF) EndIf Return $iCount EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeyGetRegistered ; Description ...: Returns array of all registered hotkey strings ; =============================================================================================================================== Func _HotkeyGetRegistered() Local $aHotkeys[UBound($g_aHotkey_Map)] For $i = 0 To UBound($g_aHotkey_Map) - 1 $aHotkeys[$i] = $g_aHotkey_Map[$i][1] Next Return $aHotkeys EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeySetDebugMode ; Description ...: Enables or disables debug logging ; =============================================================================================================================== Func _HotkeySetDebugMode($bEnabled = True) Local $bPrevious = $g_bHotkey_DebugMode $g_bHotkey_DebugMode = $bEnabled ConsoleWrite("[HotkeyUDF] Debug mode " & ($bEnabled ? "enabled" : "disabled") & @CRLF) Return $bPrevious EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeyGetStats ; Description ...: Returns performance statistics ; =============================================================================================================================== Func _HotkeyGetStats() Local $sStats = "" $sStats &= "Registered Hotkeys: " & UBound($g_aHotkey_Map) & @CRLF $sStats &= "Tracked VKs: " & UBound($g_aHotkey_TrackedVKs) & @CRLF $sStats &= "Total Checks: " & $g_iHotkey_CheckCount & @CRLF $sStats &= "Total Triggers: " & $g_iHotkey_TriggerCount & @CRLF Local $fAvg = ($g_iHotkey_CheckCount > 0) ? Round($g_iHotkey_TriggerCount / $g_iHotkey_CheckCount, 4) : 0 $sStats &= "Avg Triggers/Check: " & $fAvg & @CRLF $sStats &= "Multi-Press Window: " & $g_iHotkey_MultiPressWindow & "ms" Return $sStats EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _HotkeySetMultiPressWindow ; Description ...: Sets the time window for multi-press detection ; =============================================================================================================================== Func _HotkeySetMultiPressWindow($iMilliseconds) If $iMilliseconds < 100 Or $iMilliseconds > 2000 Then Return SetError(1, 0, False) $g_iHotkey_MultiPressWindow = $iMilliseconds If $g_bHotkey_DebugMode Then ConsoleWrite("[HotkeyUDF] Multi-press window set to " & $iMilliseconds & "ms" & @CRLF) EndIf Return True EndFunc ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __Hotkey_FindInMap($sHashKey) For $i = 0 To UBound($g_aHotkey_Map) - 1 If $g_aHotkey_Map[$i][0] = $sHashKey Then Return $i Next Return -1 EndFunc Func __Hotkey_RemoveFromMap($sHashKey) Local $iIndex = __Hotkey_FindInMap($sHashKey) If $iIndex >= 0 Then _ArrayDelete($g_aHotkey_Map, $iIndex) Return True EndIf Return False EndFunc Func __Hotkey_InitPressHistory($iVK) Local $iSize = UBound($g_aHotkey_PressHistory) ReDim $g_aHotkey_PressHistory[$iSize + 1][11] $g_aHotkey_PressHistory[$iSize][0] = $iVK For $i = 1 To 10 $g_aHotkey_PressHistory[$iSize][$i] = 0 Next EndFunc Func __Hotkey_GetPressHistoryIndex($iVK) For $i = 0 To UBound($g_aHotkey_PressHistory) - 1 If $g_aHotkey_PressHistory[$i][0] = $iVK Then Return $i Next Return -1 EndFunc Func __Hotkey_RecordPressRelease($iVK, $iNow) Local $iHistIndex = __Hotkey_GetPressHistoryIndex($iVK) If $iHistIndex < 0 Then Return Local $iEmptySlot = -1 For $i = 1 To 10 If $g_aHotkey_PressHistory[$iHistIndex][$i] = 0 Then $iEmptySlot = $i ExitLoop EndIf Next If $iEmptySlot = -1 Then For $i = 1 To 9 $g_aHotkey_PressHistory[$iHistIndex][$i] = $g_aHotkey_PressHistory[$iHistIndex][$i + 1] Next $iEmptySlot = 10 EndIf $g_aHotkey_PressHistory[$iHistIndex][$iEmptySlot] = $iNow If $g_bHotkey_DebugMode Then Local $iCount = __Hotkey_CountRecentCycles($iVK, $iNow) ConsoleWrite("[HotkeyUDF] Recorded press-release: VK=0x" & $g_aHotkey_HexCache[$iVK] & _ " | Recent cycles: " & $iCount & " | Slot: " & $iEmptySlot & @CRLF) EndIf EndFunc Func __Hotkey_CountRecentCycles($iVK, $iNow) Local $iHistIndex = __Hotkey_GetPressHistoryIndex($iVK) If $iHistIndex < 0 Then Return 0 Local $iValidCount = 0 For $i = 1 To 10 Local $iTimestamp = $g_aHotkey_PressHistory[$iHistIndex][$i] If $iTimestamp > 0 Then Local $iAge = TimerDiff($iTimestamp) If $iAge <= $g_iHotkey_MultiPressWindow Then $iValidCount += 1 EndIf EndIf Next Return $iValidCount EndFunc Func __Hotkey_ClearPressHistory($iVK, $iClearCount = 0) Local $iHistIndex = __Hotkey_GetPressHistoryIndex($iVK) If $iHistIndex < 0 Then Return If $iClearCount = 0 Then For $i = 1 To 10 $g_aHotkey_PressHistory[$iHistIndex][$i] = 0 Next If $g_bHotkey_DebugMode Then ConsoleWrite("[HotkeyUDF] Cleared ALL history: VK=0x" & $g_aHotkey_HexCache[$iVK] & @CRLF) EndIf Else Local $aSorted[10] Local $iCount = 0 For $i = 1 To 10 If $g_aHotkey_PressHistory[$iHistIndex][$i] > 0 Then $aSorted[$iCount] = $g_aHotkey_PressHistory[$iHistIndex][$i] $iCount += 1 EndIf Next If $iCount > 0 Then _ArraySort($aSorted, 0, 0, $iCount - 1) Local $iDeleteCount = ($iClearCount > $iCount) ? $iCount : $iClearCount For $i = 0 To $iDeleteCount - 1 Local $iOldest = $aSorted[$i] For $j = 1 To 10 If $g_aHotkey_PressHistory[$iHistIndex][$j] = $iOldest Then $g_aHotkey_PressHistory[$iHistIndex][$j] = 0 ExitLoop EndIf Next Next If $g_bHotkey_DebugMode Then ConsoleWrite("[HotkeyUDF] Cleared " & $iDeleteCount & " oldest timestamps: VK=0x" & $g_aHotkey_HexCache[$iVK] & @CRLF) EndIf EndIf EndIf EndFunc Func __Hotkey_CleanupHistory($iNow) Local $iCleaned = 0 For $i = 0 To UBound($g_aHotkey_PressHistory) - 1 For $j = 1 To 10 If $g_aHotkey_PressHistory[$i][$j] > 0 Then Local $iAge = TimerDiff($g_aHotkey_PressHistory[$i][$j]) If $iAge > $g_iHotkey_MultiPressWindow Then $g_aHotkey_PressHistory[$i][$j] = 0 $iCleaned += 1 EndIf EndIf Next Next If $g_bHotkey_DebugMode And $iCleaned > 0 Then ConsoleWrite("[HotkeyUDF] Cleanup: Removed " & $iCleaned & " old timestamps" & @CRLF) EndIf EndFunc Func __Hotkey_CheckTriggersForVK($iVK, $bNowPressed, $bJustPressed, $bJustReleased, $iNow) Local $iTriggered = 0 Local $iCurrentMods = 0 If $g_aHotkey_ModStates[0] Then $iCurrentMods = BitOR($iCurrentMods, 1) If $g_aHotkey_ModStates[1] Then $iCurrentMods = BitOR($iCurrentMods, 2) If $g_aHotkey_ModStates[2] Then $iCurrentMods = BitOR($iCurrentMods, 4) If $g_aHotkey_ModStates[3] Then $iCurrentMods = BitOR($iCurrentMods, 8) For $iMulti = 1 To 10 Local $sHashKey = __Hotkey_GetHashKey($iVK, $iCurrentMods, $iMulti) Local $iMapIndex = __Hotkey_FindInMap($sHashKey) If $iMapIndex >= 0 Then Local $sHotkey = $g_aHotkey_Map[$iMapIndex][1] Local $sFunction = $g_aHotkey_Map[$iMapIndex][2] Local $iFlags = $g_aHotkey_Map[$iMapIndex][3] Local $iLastTrigger = $g_aHotkey_Map[$iMapIndex][4] Local $bShouldTrigger = False Local $sTriggerReason = "" If $iMulti > 1 Then If $bJustReleased Then Local $iCycleCount = __Hotkey_CountRecentCycles($iVK, $iNow) If $g_bHotkey_DebugMode Then ConsoleWrite("[HotkeyUDF] Multi-press check: " & $sHotkey & _ " | VK=0x" & $g_aHotkey_HexCache[$iVK] & _ " | Cycles=" & $iCycleCount & "/" & $iMulti & @CRLF) EndIf If $iCycleCount >= $iMulti Then $bShouldTrigger = True $sTriggerReason = "multi-press " & $iMulti & "x (" & $iCycleCount & " cycles)" __Hotkey_ClearPressHistory($iVK, $iMulti) EndIf EndIf ElseIf BitAND($iFlags, $HOTKEY_FLAG_HOLD) Then If $bNowPressed Then Local $iHoldTime = TimerDiff($g_aHotkey_VKPressTimes[$iVK]) If $iHoldTime >= $g_iHotkey_HoldThreshold And TimerDiff($iLastTrigger) > $g_iHotkey_RepeatDelay Then $bShouldTrigger = True $sTriggerReason = "hold " & Round($iHoldTime) & "ms" EndIf EndIf ElseIf BitAND($iFlags, $HOTKEY_FLAG_RELEASE) Then If $bJustReleased Then $bShouldTrigger = True $sTriggerReason = "release" EndIf Else If $bJustPressed Then $bShouldTrigger = True $sTriggerReason = "press" ElseIf BitAND($iFlags, $HOTKEY_FLAG_REPEAT) And $bNowPressed And TimerDiff($iLastTrigger) > $g_iHotkey_RepeatDelay Then $bShouldTrigger = True $sTriggerReason = "repeat" EndIf EndIf If $bShouldTrigger Then If $g_bHotkey_DebugMode Then ConsoleWrite("[HotkeyUDF] >>> TRIGGERED: '" & $sHotkey & "' (" & $sTriggerReason & ")" & @CRLF) EndIf $g_aHotkey_Map[$iMapIndex][4] = $iNow Call($sFunction) If Not @error Then $iTriggered += 1 $g_iHotkey_TriggerCount += 1 Else ConsoleWrite("[HotkeyUDF] ERROR calling '" & $sFunction & "' (@error=" & @error & ")" & @CRLF) EndIf EndIf EndIf Next Return $iTriggered EndFunc Func __Hotkey_GetHashKey($iVK, $iModifiers, $iMultiPress) Return "VK" & $iVK & "_M" & $iModifiers & "_MP" & $iMultiPress EndFunc Func __Hotkey_ParseModifiers($sKey) Local $iModifiers = 0 Local $sTemp = $sKey While StringLen($sTemp) > 0 Local $sChar = StringLeft($sTemp, 1) If Not StringRegExp($sChar, "[!^+#]") Then ExitLoop Switch $sChar Case "^" $iModifiers = BitOR($iModifiers, 1) Case "!" $iModifiers = BitOR($iModifiers, 2) Case "+" $iModifiers = BitOR($iModifiers, 4) Case "#" $iModifiers = BitOR($iModifiers, 8) EndSwitch $sTemp = StringTrimLeft($sTemp, 1) WEnd Return $iModifiers EndFunc Func __Hotkey_StripModifiers($sKey) Local $sResult = $sKey While StringLen($sResult) > 0 And StringRegExp(StringLeft($sResult, 1), "[!^+#]") $sResult = StringTrimLeft($sResult, 1) WEnd Return $sResult EndFunc Func __Hotkey_GetVirtualKeyCode($sKey) If $sKey = "" Then Return 0 If StringLeft($sKey, 1) = "{" And StringRight($sKey, 1) = "}" Then Local $sSpecialKey = StringUpper(StringMid($sKey, 2, StringLen($sKey) - 2)) If $sSpecialKey = "" Then Return 0 Static Local $aKeyMap[][2] = [ _ ["F1", 0x70], ["F2", 0x71], ["F3", 0x72], ["F4", 0x73], _ ["F5", 0x74], ["F6", 0x75], ["F7", 0x76], ["F8", 0x77], _ ["F9", 0x78], ["F10", 0x79], ["F11", 0x7A], ["F12", 0x7B], _ ["ESC", 0x1B], ["ESCAPE", 0x1B], ["SPACE", 0x20], _ ["ENTER", 0x0D], ["RETURN", 0x0D], ["TAB", 0x09], _ ["BACKSPACE", 0x08], ["BS", 0x08], ["DELETE", 0x2E], ["DEL", 0x2E], _ ["INSERT", 0x2D], ["INS", 0x2D], ["HOME", 0x24], ["END", 0x23], _ ["PGUP", 0x21], ["PGDN", 0x22], ["UP", 0x26], ["DOWN", 0x28], _ ["LEFT", 0x25], ["RIGHT", 0x27], ["PRINTSCREEN", 0x2C], _ ["PAUSE", 0x13], ["CAPSLOCK", 0x14], ["NUMLOCK", 0x90], _ ["SCROLLLOCK", 0x91], ["LWIN", 0x5B], ["RWIN", 0x5C], _ ["APPS", 0x5D], ["CTRL", 0x11], ["ALT", 0x12], ["SHIFT", 0x10], _ ["NUMPAD0", 0x60], ["NUMPAD1", 0x61], ["NUMPAD2", 0x62], _ ["NUMPAD3", 0x63], ["NUMPAD4", 0x64], ["NUMPAD5", 0x65], _ ["NUMPAD6", 0x66], ["NUMPAD7", 0x67], ["NUMPAD8", 0x68], _ ["NUMPAD9", 0x69], ["MULTIPLY", 0x6A], ["ADD", 0x6B], _ ["SUBTRACT", 0x6D], ["DECIMAL", 0x6E], ["DIVIDE", 0x6F] _ ] For $i = 0 To UBound($aKeyMap) - 1 If $aKeyMap[$i][0] = $sSpecialKey Then Return $aKeyMap[$i][1] Next Return 0 EndIf If StringLen($sKey) = 1 Then Local $sChar = StringUpper($sKey) Local $iAscii = Asc($sChar) If ($iAscii >= 65 And $iAscii <= 90) Or ($iAscii >= 48 And $iAscii <= 57) Then Return $iAscii EndIf Static Local $aPunct[][2] = [ _ [" ", 0x20], ["`", 0xC0], ["-", 0xBD], ["=", 0xBB], _ ["[", 0xDB], ["]", 0xDD], ["\", 0xDC], [";", 0xBA], _ ["'", 0xDE], [",", 0xBC], [".", 0xBE], ["/", 0xBF] _ ] For $i = 0 To UBound($aPunct) - 1 If $aPunct[$i][0] = $sKey Then Return $aPunct[$i][1] Next EndIf Return 0 EndFunc ; Dao Van Trong - TRONG.PRO EG 1: #include "HotkeyUDF.au3" ConsoleWrite("Press Ctrl+A FOR test hotkey (ESC for Exit)" & @CRLF & @CRLF) _HotkeySet("^a", "TestFunction") _HotkeySet("{F1}", "F1") _HotkeySet("{F2}", "F2") _HotkeySet("{F3}", "F3") While Not _IsPressed("1B") ; Loop Until ESC _HotkeyCheck() Sleep(10) WEnd Func TestFunction() ConsoleWrite("Ctrl+A Is Pressed!" & @CRLF) EndFunc ;==>TestFunction Func F1() ConsoleWrite("F1 Is Pressed!" & @CRLF) EndFunc ;==>F1 Func F2() ConsoleWrite("F2 Is Pressed!" & @CRLF) EndFunc ;==>F2 Func F3() ConsoleWrite("F3 Is Pressed!" & @CRLF) EndFunc ;==>F3 EG 2: expandcollapse popup#include "HotkeyUDF.au3" ; ============================================================================ ; VÍ DỤ NÂNG CAO - NHIỀU LOẠI HOTKEY ; ============================================================================ Global $iBasicCount = 0 Global $iHoldCount = 0 Global $iReleaseCount = 0 Global $iDoubleCount = 0 ConsoleWrite("============================================" & @CRLF) ConsoleWrite(" ADVANCED HOTKEY EXAMPLE" & @CRLF) ConsoleWrite("============================================" & @CRLF & @CRLF) ; Đăng ký nhiều loại hotkey khác nhau _HotkeySet("^a", "OnBasicPress") ; Basic _HotkeySet("^{SPACE}", "OnHold", $HOTKEY_FLAG_HOLD) ; Hold _HotkeySet("{TAB}", "OnRelease", $HOTKEY_FLAG_RELEASE) ; Release _HotkeySet("^{CTRL}", "OnDouble", 0, 2) ; Double press ConsoleWrite("Hotkeys:" & @CRLF) ConsoleWrite(" Ctrl+A - Basic press" & @CRLF) ConsoleWrite(" Ctrl+Space - Hold detection (giữ 500ms)" & @CRLF) ConsoleWrite(" Tab - Release detection (thả phím)" & @CRLF) ConsoleWrite(" Double Ctrl - Multi-press (bấm 2 lần)" & @CRLF & @CRLF) ConsoleWrite("Bấm ESC để thoát" & @CRLF) ConsoleWrite("============================================" & @CRLF & @CRLF) While Not _IsPressed("1B") _HotkeyCheck() Sleep(10) WEnd ConsoleWrite(@CRLF & "Thống kê:" & @CRLF) ConsoleWrite(" Basic: " & $iBasicCount & @CRLF) ConsoleWrite(" Hold: " & $iHoldCount & @CRLF) ConsoleWrite(" Release: " & $iReleaseCount & @CRLF) ConsoleWrite(" Double: " & $iDoubleCount & @CRLF) Func OnBasicPress() $iBasicCount += 1 ConsoleWrite("[BASIC] Ctrl+A - Count: " & $iBasicCount & @CRLF) EndFunc Func OnHold() $iHoldCount += 1 ConsoleWrite("[HOLD] Ctrl+Space được giữ - Count: " & $iHoldCount & @CRLF) EndFunc Func OnRelease() $iReleaseCount += 1 ConsoleWrite("[RELEASE] Tab được thả - Count: " & $iReleaseCount & @CRLF) EndFunc Func OnDouble() $iDoubleCount += 1 ConsoleWrite(@CRLF & "*** [DOUBLE] Ctrl x2 phát hiện! Count: " & $iDoubleCount & " ***" & @CRLF & @CRLF) Beep(1000, 100) Beep(1200, 100) EndFunc Edited October 7 by Trong High performance letter removal. Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal
SOLVE-SMART Posted October 6 Posted October 6 (edited) Hi @Trong 👋 , thank you for sharing your UDF. I didn't tried your examples (and UDF) yet, but I will. Can you please explain a bit more what makes this UDF "high-performance"? No offence in any way, but I don't get it. Pressing a key or multiple keys is already applied few times in the community and already fast. Did you do a compariison between your approach and the already existing ones? I'm just curious 😀 . Regarding the set of functions that you provide I can say I like it. I personally would not use much of them, but it can be helpful for some other people - thank you. Best regards Sven Edited October 6 by SOLVE-SMART ==> AutoIt related: 🔗 GitHub, 🔗 Discord Server, 🔗 Cheat Sheet, 🔗 autoit-webdriver-boilerplate Spoiler 🌍 Au3Forums 🎲 AutoIt (en) Cheat Sheet 📊 AutoIt limits/defaults 💎 Code Katas: [...] (comming soon) 🎭 Collection of GitHub users with AutoIt projects 🐞 False-Positives 🔮 Me on GitHub 💬 Opinion about new forum sub category 📑 UDF wiki list ✂ VSCode-AutoItSnippets 📑 WebDriver FAQs 👨🏫 WebDriver Tutorial (coming soon)
Trong Posted October 7 Author Posted October 7 20 hours ago, SOLVE-SMART said: Did you do a compariison between your approach and the already existing ones? Sorry I forgot to introduce UDF: Enhanced Hotkey UDF – Non-blocking Hotkey System for AutoIt The built-in HotKeySet function in AutoIt uses system hooks that can sometimes introduce lag or block the main thread. Enhanced Hotkey UDF by Trong operates entirely non-blocking, handling all key detection in your main script loop through a single, explicit call. In just one line, you can integrate the hotkey system: While True _HotkeyCheck() ; ... your code ... WEnd Key Features Non-blocking architecture without Windows hooks or extra threads Multi-press support (1–10 presses) with customizable time window Flexible trigger flags: HOTKEY_FLAG_REPEAT for auto-repeat while holding HOTKEY_FLAG_RELEASE for trigger on key release HOTKEY_FLAG_HOLD for trigger after holding a threshold Debug mode and performance stats (checks, triggers, average triggers per check) Easy management: register, remove, clear all, list registered hotkeys Quick Comparison | Criterion | HotKeySet (AutoIt) | Enhanced Hotkey UDF | |-----------------------------------------|-----------------------------------------|---------------------------------------------------------| | Architecture | System-level hook, blocking | Polling-based, non-blocking | | Impact on main thread | May cause lag | Zero blocking, full control | | Multi-press (double, etc.) | Not supported | Supported (1–10 presses) | | Hold detection | Not supported | Supported with configurable threshold | | Release detection | Not supported | Supported | | Auto-repeat while holding | Not supported | Supported (`HOTKEY_FLAG_REPEAT`) | | Debug and stats | No | Yes | Conclusion If you need a powerful hotkey manager for scripts, games, or real-time applications that avoids blocking your main thread while offering press, release, hold, repeat, and multi-press detection, the Enhanced Hotkey UDF is the tool of choice. Simply call _HotkeyCheck in your loop and let the UDF handle the rest. PS: This article was analyzed and created by AI (Copilot). ioa747 and SEKOMD 2 Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal
nitekram Posted October 24 Posted October 24 Has this been tested in windows 11, as I have found, not fully tested, that hotkeys running in an app, located in the program files directory, do not pickup, but if I move that same app outside of the program folder, say to the desktop, they work. 2¢ All by me:"Sometimes you have to go back to where you started, to get to where you want to go." "Everybody catches up with everyone, eventually" "As you teach others, you are really teaching yourself." From my dad "Do not worry about yesterday, as the only thing that you can control is tomorrow." WIKI | Tabs; | Arrays; | Strings | Wiki Arrays | How to ask a Question | Forum Search | FAQ | Tutorials | Original FAQ | ONLINE HELP | UDF's Wiki | AutoIt PDF AutoIt Snippets | Multple Guis | Interrupting a running function | Another Send StringRegExp | StringRegExp Help | RegEXTester | REG TUTOR | Reg TUTOT 2 AutoItSetOption | Macros | AutoIt Snippets | Wrapper | Autoit Docs SCITE | SciteJump | BB | MyTopics | Programming | UDFs | AutoIt 123 | UDFs Form | UDF Learning to script | Tutorials | Documentation | IE.AU3 | Games? | FreeSoftware | Path_Online | Core Language Programming Tips Excel Changes ControlHover.UDF GDI_Plus Draw_On_Screen GDI Basics GDI_More_Basics GDI Rotate GDI Graph GDI CheckExistingItems GDI Trajectory Replace $ghGDIPDll with $__g_hGDIPDll DLL 101? Array via Object GDI Swimlane GDI Plus French 101 Site GDI Examples UEZ GDI Basic Clock GDI Detection Ternary operator
nitekram Posted yesterday at 11:40 AM Posted yesterday at 11:40 AM On 10/24/2025 at 9:43 AM, nitekram said: Has this been tested in windows 11, as I have found, not fully tested, that hotkeys running in an app, located in the program files directory, do not pickup, but if I move that same app outside of the program folder, say to the desktop, they work. I have found that it was not hotkeys that was causing the issue in program folders, but the screencapture that is not allowed, unless you are running as an admin 2¢ All by me:"Sometimes you have to go back to where you started, to get to where you want to go." "Everybody catches up with everyone, eventually" "As you teach others, you are really teaching yourself." From my dad "Do not worry about yesterday, as the only thing that you can control is tomorrow." WIKI | Tabs; | Arrays; | Strings | Wiki Arrays | How to ask a Question | Forum Search | FAQ | Tutorials | Original FAQ | ONLINE HELP | UDF's Wiki | AutoIt PDF AutoIt Snippets | Multple Guis | Interrupting a running function | Another Send StringRegExp | StringRegExp Help | RegEXTester | REG TUTOR | Reg TUTOT 2 AutoItSetOption | Macros | AutoIt Snippets | Wrapper | Autoit Docs SCITE | SciteJump | BB | MyTopics | Programming | UDFs | AutoIt 123 | UDFs Form | UDF Learning to script | Tutorials | Documentation | IE.AU3 | Games? | FreeSoftware | Path_Online | Core Language Programming Tips Excel Changes ControlHover.UDF GDI_Plus Draw_On_Screen GDI Basics GDI_More_Basics GDI Rotate GDI Graph GDI CheckExistingItems GDI Trajectory Replace $ghGDIPDll with $__g_hGDIPDll DLL 101? Array via Object GDI Swimlane GDI Plus French 101 Site GDI Examples UEZ GDI Basic Clock GDI Detection Ternary operator
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now