Simulate calling hotkeys

Just starting out? Need help? Post your questions and find answers here.
AZJIO
Addict
Addict
Posts: 1318
Joined: Sun May 14, 2017 1:48 am

Simulate calling hotkeys

Post by AZJIO »

I have several programs written in AutoIt3 that use hotkey calling. I use them to process text to the left of the cursor.
To do this, I call the following hotkeys:
Ctrl + Shift + Left
Ctrl + Insert
... process ...
Shift + Insert

It's unstable. I tried for a long time to adjust the pauses between keystrokes, but there is no pattern in the behavior, the action occurs every other time. Now I would like to use this for the AutoCompletion program. The program on AutoIt3 works reliably, it will work 10 times out of 10. It has a key sticking check, but that's not the point, even if the code below fails the second time, but it fails the first time. That is, key sticking can be excluded.

Code: Select all

; AZJIO
; convert text typed in the wrong keyboard layout

EnableExplicit

Global En$ = "`qwertyuiop[]asdfghjkl;'zxcvbnm,./~QWERTYUIOP{}ASDFGHJKL:" + Chr(34) + "|ZXCVBNM<>?@#$^&"
Global Ru$ = "ёйцукенгшщзхъфывапролджэячсмитьбю.ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭ/ЯЧСМИТЬБЮ," + Chr(34) + "№;:?"
Global EnT$ = "qwertyuiopasdfghjklzxcvbnm"
Global RuT$ = "ёйцукенгшщзхъфывапролджэячсмитьбю"
Global Lang = 0
Global Dim aSel.s{1}(0)

#GadgetHK = 0
#GadgetBtn = 1
#HK_ID = 1001

Define HotkeyCode
Define VirtKey
Define ModKey

; Macro MAKELONG(loword, hiword)
;   (hiword << 16 | loword)
; EndMacro

Procedure.s TrimLeft(*a, n)
	Protected *p.string = @*a
	*p\s = Right(*p\s, Len(*p\s) - n)
EndProcedure

Procedure StrToArrLetter(Array Arr.s{1}(1), String$)
	Protected LenStr, i
	LenStr = Len(String$)
	If LenStr
		ReDim Arr(LenStr - 1)
		PokeS(Arr(), String$, -1, #PB_String_NoZero)
	EndIf
	ProcedureReturn
EndProcedure

Procedure.s GetKey(HotkeyCode)
	Protected HiW, Key.s
	HiW = HotkeyCode >> 16
	If HiW & #HOTKEYF_CONTROL
		Key + " + Ctrl"
	EndIf
	If HiW & #HOTKEYF_SHIFT
		Key + " + Shift"
	EndIf
	If HiW & #HOTKEYF_ALT
		Key + " + Alt"
	EndIf
	Key + " + " + Chr(HotkeyCode & $FFFF)
	TrimLeft(@Key, 3)
	ProcedureReturn Key
EndProcedure

Procedure GetModKey(MOD)
	Protected ModKey = 0
	If MOD & #HOTKEYF_SHIFT
		ModKey | #MOD_SHIFT
	EndIf
	If MOD & #HOTKEYF_CONTROL
		ModKey | #MOD_CONTROL
	EndIf
	If MOD & #HOTKEYF_ALT
		ModKey | #MOD_ALT
	EndIf
	ProcedureReturn ModKey
EndProcedure

; Ctrl + Insert
Procedure SendHotKey2(mod, key, sleep = 20, sleep1=1)
	keybd_event_(mod,0,#KEYEVENTF_EXTENDEDKEY,0)
	Delay(sleep1)
	keybd_event_(key, 0, #KEYEVENTF_EXTENDEDKEY, 0)
	Delay(sleep)
	keybd_event_(key,0,#KEYEVENTF_KEYUP | #KEYEVENTF_EXTENDEDKEY,0)
	Delay(sleep1)
	keybd_event_(mod,0,#KEYEVENTF_KEYUP | #KEYEVENTF_EXTENDEDKEY,0)
EndProcedure

; Ctrl + Insert
; I was hoping that it would work faster
Procedure SendHotKeyCtrlIns(sleep = 20, sleep1=110)
	Protected hWnd = GetForegroundWindow_()
; 	keybd_event_(#VK_CONTROL,0,0,0)
	SendMessage_(hWnd, #WM_KEYDOWN, #VK_CONTROL, 0)
	Delay(sleep1)
	SendMessage_(hWnd, #WM_KEYDOWN, #VK_INSERT, 0)
	Delay(sleep1)
	SendMessage_(hWnd, #WM_KEYUP, #VK_INSERT, 0)
	Delay(sleep1)
; 	keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
	SendMessage_(hWnd, #WM_KEYUP, #VK_CONTROL, 0)
EndProcedure

; Ctrl + Shift + Left
; Procedure SendHotKeyCtrlShiftL(sleep = 20, sleep1=3)
; 	keybd_event_(#VK_CONTROL,0,0,0)
; 	Delay(sleep1)
; 	keybd_event_(#VK_SHIFT,0,0,0)
; 	Delay(sleep1)
; 	keybd_event_(#VK_LEFT, 0, 0, 0)
; 	Delay(sleep)
; 	keybd_event_(#VK_LEFT,0,#KEYEVENTF_KEYUP,0)
; 	Delay(sleep1)
; 	keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)
; 	Delay(sleep1)
; 	keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
; EndProcedure

; Ctrl + Shift + Left
Procedure SendHotKeyCtrlShiftL(sleep = 20, sleep1=3)
        keybd_event_(#VK_SHIFT,0,#KEYEVENTF_EXTENDEDKEY,0)
        Delay(sleep1)
        keybd_event_(#VK_CONTROL,0,#KEYEVENTF_EXTENDEDKEY,0)
        Delay(sleep1)
        keybd_event_(#VK_LEFT, 0, #KEYEVENTF_EXTENDEDKEY, 0)
        Delay(sleep)
        keybd_event_(#VK_LEFT,0,#KEYEVENTF_KEYUP | #KEYEVENTF_EXTENDEDKEY,0)
        Delay(sleep1)
        keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP | #KEYEVENTF_EXTENDEDKEY,0)
        Delay(sleep1)
        keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP | #KEYEVENTF_EXTENDEDKEY,0)
EndProcedure

Procedure _Re(mode)
	Protected Old_bufer$, Selected_Text$, Letter$, i, New_Text$, n, Layout
	Old_bufer$ = GetClipboardText()
	SetClipboardText("")
	SendHotKey2(#VK_CONTROL, #VK_INSERT, 110, 50)
	; SendHotKeyCtrlIns()
	Selected_Text$ = GetClipboardText()
	If Not Len(Left(Selected_Text$ ,1))
		SendHotKeyCtrlShiftL()
		Delay(90)
		SendHotKey2(#VK_CONTROL, #VK_INSERT, 110, 50)
		; SendHotKeyCtrlIns()
		Delay(30)
		Selected_Text$ = GetClipboardText()
	EndIf
	
	If Not Len(Left(Selected_Text$ ,1))
		ProcedureReturn
	EndIf
		; ищем справа-налево последний валидный символ в тексте, по которому можно определить язык
	Lang = 0
		Letter$ = Mid(Selected_Text$, i, 1)
	For i = Len(Selected_Text$) To 1 Step -1
		If FindString(EnT$, Letter$)
			Lang = 409
			Break
		EndIf
		If FindString(RuT$, Letter$)
			Lang = 419
			Break
		EndIf
	Next

	If Not Lang
		ProcedureReturn
	EndIf

	StrToArrLetter(aSel(), Selected_Text$)
	New_Text$ = ""

	Select mode
		Case 0

			; if Russian, then change to English.
			If Lang = 419
				For i = 0 To ArraySize(aSel())
					n = FindString(Ru$, aSel(i), 1, #PB_String_CaseSensitive)
					If n = 0
						New_Text$ + aSel(i)
					Else
						New_Text$ + Mid(En$, n, 1)
					EndIf
				Next
				Layout = LoadKeyboardLayout_("00000409", #KLF_ACTIVATE)
				SendMessage_(GetForegroundWindow_(), #WM_INPUTLANGCHANGEREQUEST, 1, Layout)
			; if English, then change to Russian
			ElseIf Lang = 409
				For i = 0 To ArraySize(aSel())
					n = FindString(En$, aSel(i), 1, #PB_String_CaseSensitive)
					If n = 0
						New_Text$ + aSel(i)
					Else
						New_Text$ + Mid(Ru$, n, 1)
					EndIf
				Next
				Layout = LoadKeyboardLayout_("00000419", #KLF_ACTIVATE)
				SendMessage_(GetForegroundWindow_(), #WM_INPUTLANGCHANGEREQUEST, 1, Layout)
			EndIf
; 		Case 1
; 			SendHotKey2(#VK_CONTROL, #VK_INSERT, 200, 100)
; 		Case 2
; 			SendHotKey2(#VK_SHIFT, #VK_HOME)
; 			Delay(100)
; 			SendHotKey2(#VK_CONTROL, #VK_INSERT, 200, 100)
	EndSelect
	

	If New_Text$ <> Selected_Text$
		SetClipboardText(New_Text$)
		Delay(10)
		SendHotKey2(#VK_SHIFT, #VK_INSERT, 110, 30)
	Else
		keybd_event_(#VK_RIGHT, 0, 0, 0)
		Delay(10)
		keybd_event_(#VK_RIGHT,0,#KEYEVENTF_KEYUP,0)
	EndIf
; 	Delay(90)
	
	; SendKeys(0,"PureBasic 5.70 LTS (x86) - я текст корекция.pb","{CONTROLDOWN}{INSERT}{CONTROLUP}")
; 	Selected_Text$ = GetClipboardText()
EndProcedure


If OpenWindow(0, 0, 0, 240, 70, "Gadget Hotkeys", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	ShortcutGadget(#GadgetHK, 10, 10, 200, 25, #PB_Shortcut_Alt | #PB_Shortcut_J)
	; 	SetGadgetState(0 , #PB_Shortcut_Control | #PB_Shortcut_B) ; can be inserted this way
	ButtonGadget(#GadgetBtn, 10, 40, 100, 28, "Apply")
	Repeat
		Select WaitWindowEvent()
			Case #WM_HOTKEY
				Select EventwParam()
					Case #HK_ID
						_Re(0)
						Debug "Intercepted hotkey 1"
					Case 2
						Debug "Intercepted hotkey 2"
					Case 3
						Debug "Intercepted hotkey 3"
				EndSelect
			Case #PB_Event_Gadget
				Select EventGadget()
					Case #GadgetBtn
						HotkeyCode = GetGadgetState(#GadgetHK)
						If Not HotkeyCode
							Debug "Cancel the hotkey (Backspace)"
							UnregisterHotKey_(WindowID(0), #HK_ID)
						EndIf
						Debug HotkeyCode
						VirtKey = HotkeyCode & $FFFF ; LoWord
						ModKey = GetModKey(HotkeyCode >> 16)
						Debug GetKey(HotkeyCode)
						; Debug SendMessage_(GadgetID(#GadgetHK), #HKM_GETHOTKEY, 0, 0)
						; If MAKELONG(loword, hiword)
						UnregisterHotKey_(WindowID(0), #HK_ID) ; reassignment works without unregisteringreassignment works without unregistering
						If Not RegisterHotKey_(WindowID(0), #HK_ID, ModKey, VirtKey)
							Debug "Failed to register hotkey"
						EndIf
				EndSelect
			Case #PB_Event_CloseWindow
				UnregisterHotKey_(WindowID(0), #HK_ID)
				CloseWindow(0)
				End
		EndSelect
	ForEver
EndIf
.
.

Code: Select all

; AZJIO
; convert text typed in the wrong keyboard layout

EnableExplicit

Global En$ = "`qwertyuiop[]asdfghjkl;'zxcvbnm,./~QWERTYUIOP{}ASDFGHJKL:" + Chr(34) + "|ZXCVBNM<>?@#$^&"
Global Ru$ = "ёйцукенгшщзхъфывапролджэячсмитьбю.ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭ/ЯЧСМИТЬБЮ," + Chr(34) + "№;:?"
Global EnT$ = "qwertyuiopasdfghjklzxcvbnm"
Global RuT$ = "ёйцукенгшщзхъфывапролджэячсмитьбю"
Global Lang = 0
Global Dim aSel.s{1}(0)

#GadgetHK = 0
#GadgetBtn = 1
#HK_ID = 1001

Define HotkeyCode
Define VirtKey
Define ModKey

; Macro MAKELONG(loword, hiword)
;   (hiword << 16 | loword)
; EndMacro

Procedure.s TrimLeft(*a, n)
	Protected *p.string = @*a
	*p\s = Right(*p\s, Len(*p\s) - n)
EndProcedure

Procedure StrToArrLetter(Array Arr.s{1}(1), String$)
	Protected LenStr, i
	LenStr = Len(String$)
	If LenStr
		ReDim Arr(LenStr - 1)
		PokeS(Arr(), String$, -1, #PB_String_NoZero)
	EndIf
	ProcedureReturn
EndProcedure

Procedure.s GetKey(HotkeyCode)
	Protected HiW, Key.s
	HiW = HotkeyCode >> 16
	If HiW & #HOTKEYF_CONTROL
		Key + " + Ctrl"
	EndIf
	If HiW & #HOTKEYF_SHIFT
		Key + " + Shift"
	EndIf
	If HiW & #HOTKEYF_ALT
		Key + " + Alt"
	EndIf
	Key + " + " + Chr(HotkeyCode & $FFFF)
	TrimLeft(@Key, 3)
	ProcedureReturn Key
EndProcedure

Procedure GetModKey(MOD)
	Protected ModKey = 0
	If MOD & #HOTKEYF_SHIFT
		ModKey | #MOD_SHIFT
	EndIf
	If MOD & #HOTKEYF_CONTROL
		ModKey | #MOD_CONTROL
	EndIf
	If MOD & #HOTKEYF_ALT
		ModKey | #MOD_ALT
	EndIf
	ProcedureReturn ModKey
EndProcedure

Global Dim KeyArray.INPUT( 0 )
Global *G_key.INPUT = @KeyArray( 0 )
*G_key\type = #INPUT_KEYBOARD

Procedure ReleaseKey()
	While GetAsyncKeyState_(#VK_SHIFT)
		*G_key\ki\wVk = #VK_SHIFT
		*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
		SendInput_( 1, KeyArray(), 40 )
		Delay(100)
	Wend
	While GetAsyncKeyState_(#VK_CONTROL)
		*G_key\ki\wVk = #VK_CONTROL
		*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
		SendInput_( 1, KeyArray(), 40 )
		Delay(100)
	Wend
	While GetAsyncKeyState_(#VK_MENU)
		*G_key\ki\wVk = #VK_MENU
		*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
		SendInput_( 1, KeyArray(), 40 )
		Delay(100)
	Wend
EndProcedure

Procedure SendNormalKey( keycode )
	*G_key\ki\wVk = keycode
	*G_key\ki\dwFlags = 0 ; 0 for key press.
	SendInput_( 1, KeyArray(), 40 )
	
	*G_key\ki\wVk = keycode
	*G_key\ki\dwFlags = #KEYEVENTF_KEYUP ; #KEYEVENTF_KEYUP for key release.
	SendInput_( 1, KeyArray(), 40 )
EndProcedure

Procedure SendSpecialKey( keycode, special_keycode )
	ReleaseKey()
	*G_key\ki\wVk = special_keycode
	*G_key\ki\dwFlags = 0
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = keycode
	*G_key\ki\dwFlags = 0
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = keycode
	*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = special_keycode
	*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
	SendInput_( 1, KeyArray(), 40 )
EndProcedure

Procedure SendSpecialKey2( keycode, special_keycode, special_keycode2 )
	ReleaseKey()
	*G_key\ki\wVk = special_keycode
	*G_key\ki\dwFlags = 0
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = special_keycode2
	*G_key\ki\dwFlags = 0
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = keycode
	*G_key\ki\dwFlags = 0
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = keycode
	*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = special_keycode2
	*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
	SendInput_( 1, KeyArray(), 40 )

	*G_key\ki\wVk = special_keycode
	*G_key\ki\dwFlags = #KEYEVENTF_KEYUP
	SendInput_( 1, KeyArray(), 40 )
EndProcedure

Procedure _Re(mode)
	Protected Old_bufer$, Selected_Text$, Letter$, i, New_Text$, n, Layout
	Old_bufer$ = GetClipboardText()
	SetClipboardText("")
	SendSpecialKey(#VK_INSERT, #VK_CONTROL)
	Selected_Text$ = GetClipboardText()
	If Not Len(Left(Selected_Text$ ,1))
		SendSpecialKey2(#VK_LEFT, #VK_CONTROL, #VK_SHIFT)
		Delay(90)
		SendSpecialKey(#VK_INSERT, #VK_CONTROL)
		Delay(30)
		Selected_Text$ = GetClipboardText()
	EndIf

	If Not Len(Left(Selected_Text$ ,1))
		ProcedureReturn
	EndIf
		; ищем справа-налево последний валидный символ в тексте, по которому можно определить язык
	Lang = 0
		Letter$ = Mid(Selected_Text$, i, 1)
	For i = Len(Selected_Text$) To 1 Step -1
		If FindString(EnT$, Letter$)
			Lang = 409
			Break
		EndIf
		If FindString(RuT$, Letter$)
			Lang = 419
			Break
		EndIf
	Next

	If Not Lang
		ProcedureReturn
	EndIf

	StrToArrLetter(aSel(), Selected_Text$)
	New_Text$ = ""

	Select mode
		Case 0

			; if Russian, then change to English.
			If Lang = 419
				For i = 0 To ArraySize(aSel())
					n = FindString(Ru$, aSel(i), 1, #PB_String_CaseSensitive)
					If n = 0
						New_Text$ + aSel(i)
					Else
						New_Text$ + Mid(En$, n, 1)
					EndIf
				Next
				Layout = LoadKeyboardLayout_("00000409", #KLF_ACTIVATE)
				SendMessage_(GetForegroundWindow_(), #WM_INPUTLANGCHANGEREQUEST, 1, Layout)
			; if English, then change to Russian
			ElseIf Lang = 409
				For i = 0 To ArraySize(aSel())
					n = FindString(En$, aSel(i), 1, #PB_String_CaseSensitive)
					If n = 0
						New_Text$ + aSel(i)
					Else
						New_Text$ + Mid(Ru$, n, 1)
					EndIf
				Next
				Layout = LoadKeyboardLayout_("00000419", #KLF_ACTIVATE)
				SendMessage_(GetForegroundWindow_(), #WM_INPUTLANGCHANGEREQUEST, 1, Layout)
			EndIf
; 		Case 1
; 			SendSpecialKey(#VK_INSERT, #VK_CONTROL)
; 		Case 2
; 			SendHotKey2(#VK_SHIFT, #VK_HOME)
; 			SendSpecialKey(#VK_HOME, #VK_SHIFT)
; 			Delay(100)
; 			SendSpecialKey(#VK_INSERT, #VK_CONTROL)
	EndSelect


	If New_Text$ <> Selected_Text$
		SetClipboardText(New_Text$)
		Delay(10)
		SendSpecialKey(#VK_INSERT, #VK_SHIFT)
	Else
		SendNormalKey(#VK_RIGHT)
	EndIf
EndProcedure


If OpenWindow(0, 0, 0, 240, 70, "Gadget Hotkeys", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	ShortcutGadget(#GadgetHK, 10, 10, 200, 25, #PB_Shortcut_Alt | #PB_Shortcut_J)
	; 	SetGadgetState(0 , #PB_Shortcut_Control | #PB_Shortcut_B) ; can be inserted this way
	ButtonGadget(#GadgetBtn, 10, 40, 100, 28, "Apply")
	Repeat
		Select WaitWindowEvent()
			Case #WM_HOTKEY
				Select EventwParam()
					Case #HK_ID
						_Re(0)
						Debug "Intercepted hotkey 1"
					Case 2
						Debug "Intercepted hotkey 2"
					Case 3
						Debug "Intercepted hotkey 3"
				EndSelect
			Case #PB_Event_Gadget
				Select EventGadget()
					Case #GadgetBtn
						HotkeyCode = GetGadgetState(#GadgetHK)
						If Not HotkeyCode
							Debug "Cancel the hotkey (Backspace)"
							UnregisterHotKey_(WindowID(0), #HK_ID)
						EndIf
						Debug HotkeyCode
						VirtKey = HotkeyCode & $FFFF ; LoWord
						ModKey = GetModKey(HotkeyCode >> 16)
						Debug GetKey(HotkeyCode)
						; Debug SendMessage_(GadgetID(#GadgetHK), #HKM_GETHOTKEY, 0, 0)
						; If MAKELONG(loword, hiword)
						UnregisterHotKey_(WindowID(0), #HK_ID) ; reassignment works without unregisteringreassignment works without unregistering
						If Not RegisterHotKey_(WindowID(0), #HK_ID, ModKey, VirtKey)
							Debug "Failed to register hotkey"
						EndIf
				EndSelect
			Case #PB_Event_CloseWindow
				UnregisterHotKey_(WindowID(0), #HK_ID)
				CloseWindow(0)
				End
		EndSelect
	ForEver
EndIf