Trong Posted Sunday at 11:59 PM Posted Sunday at 11:59 PM (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 6 hours ago by Trong High performance letter removal. Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal
SOLVE-SMART Posted yesterday at 04:39 AM Posted yesterday at 04:39 AM (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 yesterday at 05:11 AM 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 6 hours ago Author Posted 6 hours ago 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 1 Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal
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