Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Multiple keyboards workaround


  • Please log in to reply
6 replies to this topic
XX0
  • Members
  • 32 posts
  • Last active: Dec 12 2014 01:05 PM
  • Joined: 17 Jul 2010
The problem
AutoHotkey doesn't natively support the differentiation between multiple keyboards. AHKHID tackles this problem but does not allow intercepting or eating the keypress. Alternatives include Quick Macros and GlovePIE, but that requires the learning of a new syntax. The final alternative then is HID macros which doesn't require the learning of a new language but is also very limited in its actions. To overcome this limitation you could have HID macros intercept a keypress and have it send some key combination like Alt+F15 that triggers AutoHotkey. If you require many keypads, you need something more extensive.

My solution
I have written a AutoHotkey script that passes its startup parameters to a running AutoHotkey instance for processing. HID macros intercepts the keys and runs the script with the keyboard ID and Key ID as parameters. This way keys are intercepted AND the keyboard is identified.

Another downside of HID macros is that it requires each key to be configured and it takes 11 lines of configuration per key. That's why I've also written a script to generate the configuration for HID macros.

Why
The motivation behind the script is that I want to place multiple USB keypads around the house all connected to the home server. The keypads control lights or locks switched by a relay board. Different keypads should switch different lighting and outside keypads should block after incorrect entries.

How to use
Copy the scrips to a file using the same names as provided.
Download HID macros to %A_ScriptDir%\HidMacros.
Run HIDmacros_KeypadScriptGenerator.ahk and go through the wizard, the HID macros script will be pasted to the clipboard.
Create a file %A_ScriptDir%\HidMacros\HidMacros.ini, paste the clipboard and save.
Run HIDmacros.ahk.
Configure the keypads in HID macros.
Hide HID macros by double clicking the tray icon.When using the code, but keep in mind: It's designed for keyPADS not keyBOARDS. If you wan't to use a full keyboard you need to add a LOT of extra keys to all three scripts.

HIDmacros_send.ahk
#NoTrayIcon
SetTitleMatchMode,2
DetectHiddenWindows,On
if 0=2 	;%0% holds the amount of parameters passed, if it equals 2, send the message
		;%1% holds the keyboard number
		;%2% holds the VK number
{
	SendMessage,0x4530,%1%,%2%,,HIDmacros.ahk ahk_class AutoHotkey
}
ExitApp

HIDmacros.ahk Here I've also put some examples on how to detect a key presses or sequence inputs.
SetNumlockState,AlwaysOn
HIDmacrosexe=%A_ScriptDir%\HidMacros\HidMacros.exe
Menu,Tray,Icon,%HIDmacrosexe%
Gosub,CheckHIDmacros
SetTimer,CheckHIDmacros,60000
OnExit,ExitShow
OnMessage(0x4530, "InputMsg")

;Begin definition VK codes:
			VK96=0
			VK97=1
			VK98=2
			VK99=3
			VK100=4
			VK101=5
			VK102=6
			VK103=7
			VK104=8
			VK105=9
			VK13=Enter
			VK8=BackSpace
			VK106=`*
			VK107=`+
			VK109=`-
			VK111=`/
			VK110=`.
			VK33=PgUp
			VK34=PgDn
			VK35=End
			VK36=Home
			VK37=Left
			VK38=Up
			VK39=Right
			VK40=Down
			VK45=Insert
			VK46=Delete
			VK144=NumLock
;End definition VK codes:


InputMsg(KeyboardID,KeyID) {
	;Critical
	Static Sequence1, Sequence2, Sequence3, TimeOfInput1, TimeOfInput2, TimeOfInput3
	KeyID:=VK%KeyID%
	TimeOfInputDifference:=TimeOfInput%KeyboardID%
	TimeOfInputDifference-=A_Now,Seconds
	TimeOfInputDifference*=-1
	TimeOfInput%KeyboardID%:=A_Now
	
	If (TimeOfInputDifference>20)
		Sequence%KeyboardID%=
		
	If (KeyID="BackSpace")
		Sequence%KeyboardID%=
	Else If (KeyID="Enter"){
		Sequence:=Sequence%KeyboardID%
		ProcessSequence(KeyboardID,Sequence)
		Sequence%KeyboardID%=
	}Else If (KeyID="-"){
		MsgBox,,Action,Lights ON,2
		SetTimer,LightsOff,-30000
	}Else If (KeyID="/"){
		MsgBox,,Action,Bell,2
	}Else
		Sequence%KeyboardID% .= KeyID
}

ProcessSequence(KeyboardID,Sequence){
	static TimeOfInvalid1, TimeOfInvalid2, TimeOfInvalid3
	TimeOfInvalidDifference:=TimeOfInvalid%KeyboardID%
	TimeOfInvalidDifference-=A_Now,Seconds
	TimeOfInvalidDifference*=-1
	StringSplit,Parameter,Sequence,.
	If (TimeOfInvalidDifference<20) AND (TimeOfInvalidDifference<>0){
		MsgBox,,,BLOCKED,2
		Exit
	}
	If ((KeyboardID=2) OR (KeyboardID=3)) AND (Parameter1=123)
		MsgBox,,Action,Alarm ARMED,2
	Else If (Parameter1=456)
		MsgBox,,Action,Alarm DISARMED,2
	Else If (Parameter1=147896)
		MsgBox,,Action,Door Open,2
	Else If (Parameter1="")
		MsgBox,,Action,Empty,2
	Else If (KeyboardID=2){
		TimeOfInvalid%KeyboardID%:=A_Now
		MsgBox,,,INVALID: %Parameter1%,2
	}
}

#IfWinNotExist,HID macros ahk_class THIDMacrosForm ;HID macros not running! Block all keypads keys
NumPad0::
NumPad1::
NumPad2::
NumPad3::
NumPad4::
NumPad5::
NumPad6::
NumPad7::
NumPad8::
NumPad9::
NumpadDot::
NumpadDiv::
NumpadMult::
NumpadAdd::
NumpadSub::
NumpadIns::
NumpadEnd::
NumpadDown::
NumpadPgDn::
NumpadLeft::
NumpadClear::
NumpadRight::
NumpadHome::
NumpadUp::
NumpadPgUp::
NumpadDel::
NumLock::
Return
NumpadEnter::Gosub,CheckHIDmacros

CheckHIDmacros:
	SetTitleMatchMode,2
	DetectHiddenWindows,Off
	if (A_TimeIdlePhysical > 60000) AND WinExist("HID macros ahk_class THIDMacrosForm")
		Gosub,HideHID
	DetectHiddenWindows,On
	If !WinExist("HID macros ahk_class THIDMacrosForm"){
		Gosub,ShowHID
		Gosub,HideHID
	}Else{
		Menu,Tray,Add,Hide HID macros,HideHID
		Menu,Tray,Default,Hide HID macros
	}
Return
ShowHID:
	DetectHiddenWindows,On
	If !WinExist("HID macros ahk_class THIDMacrosForm"){
		Run,%HIDmacrosexe%
		Menu,Tray,Add,Show HID macros,ShowHID
		WinWait,HID macros,,2
	}
	GroupAdd,HIDmacros,HID macros
	WinShow,ahk_group HIDmacros
	WinActivate,ahk_group HIDmacros
	Menu,Tray,Delete,Show HID macros
	Menu,Tray,Add,Hide HID macros,HideHID
	Menu,Tray,Default,Hide HID macros
Return
HideHID:
	GroupAdd,HIDmacros,HID macros
	WinHide,ahk_group HIDmacros
	Menu,Tray,Delete,Hide HID macros
	Menu,Tray,Add,Show HID macros,ShowHID
	Menu,Tray,Default,Show HID macros
Return
ExitShow:
	DetectHiddenWindows,Off
	If !WinExist("HID macros ahk_class THIDMacrosForm")
		Gosub,ShowHID
	ExitApp
Return


LightsOff:
	MsgBox,,Action,Lights OFF,2
Return
#IfWinActive

HIDmacros_KeypadScriptGenerator.ahk
;Set variables:
			VK96=0
			VK97=1
			VK98=2
			VK99=3
			VK100=4
			VK101=5
			VK102=6
			VK103=7
			VK104=8
			VK105=9
			VK13=Enter
			VK8=BackSpace
			VK106=`*
			VK107=`+
			VK109=`-
			VK111=`/
			VK110=`.
			VK33=PgUp
			VK34=PgDn
			VK35=End
			VK36=Home
			VK37=Left
			VK38=Up
			VK39=Right
			VK40=Down
			VK45=Insert
			VK46=Delete
			VK144=NumLock
			macroindex=0
			configuration=
			keycodes:="8 13 96 97 98 99 100 101 102 103 104 105 106 107 109 110 111" ;NumLock On virtual key codes
			;keycodes.=" 144" ;NumLock, does not work on my PC

Loop{
	InputBox, keypad, Keypad name, Enter the name of keypad #%A_Index%.`nLeave blank to continue.
	If (keypad=""){
		keypad:=A_Index-1
		Break
	}
	keypad%A_Index%:=keypad
	If (ErrorLevel)
		ExitApp
}

InputBox, exe, AutoHotkey executable location, Enter the location of the AutoHotkey executable file,,,,,,,, C:\Program Files (x86)\AutoHotkey\AutoHotkey.exe
If (ErrorLevel)
	ExitApp
	
InputBox, script, AutoHotkey script location, Enter the location of the HIDmacros_send.ahk file,,,,,,,, C:\Program Files (x86)\AutoHotkey\Domotica\HIDmacros_send.ahk
If (ErrorLevel)
	ExitApp

MsgBox, 291, NumLock off state, Should numlock off keys be included?
IfMsgBox Yes
	keycodes.=" 33 34 35 36 37 38 39 40 45 46" ;NumLock Off virtual key codes
Else IfMsgBox Cancel
	ExitApp
	
Loop,%keypad%
{
	keypadname:=keypad%A_Index%
	keypadnumber:=A_Index
	
	Loop, Parse, keycodes, %A_Space%
	{
		keyname:=VK%A_LoopField%
		configuration.="[Macro." . macroindex . "]`r`n"
		configuration.="Keyboard=" . keypadname . "`r`n"
		configuration.="Name=" . keypadname . ": " . keyname . "`r`n"
		configuration.="KeyCode=" . A_LoopField . "`r`n"
		configuration.="Direction=down`r`n"
		configuration.="Action=CMD`r`n"
		configuration.="Sequence=`r`n"
		configuration.="SCEvent=`r`n"
		configuration.="SCText=0`r`n"
		configuration.="SCParams=`r`n"
		configuration.="Command=""" . exe . """ """ . script . """ " . keypadnumber . " " . A_LoopField . "`r`n"
		macroindex++
	}	
}

Clipboard:=configuration
MsgBox, 64, Configuration completed, The HidMacros key configuration has been copied to the clipboard, 5

Perhaps not the neatest or well commented code, but two month ago I've never heard of AHK. So your input is appreciated.

Larry1976
  • Guests
  • Last active:
  • Joined: --
Hi,

it's a GREAT script!!! ... but i've an issue...

I need to be able to use the script "HIDmacros_send.ahk" in fast & rapid sequence... and when i try it... the CPU reaches 100% usage, as they are running too many concurrent processes.
Do you think, there is a solution about it?

(my application needs 2 usb NumPads)

XX0
  • Members
  • 32 posts
  • Last active: Dec 12 2014 01:05 PM
  • Joined: 17 Jul 2010
I should have foreseen that problem when I launch a process on every key press :oops:.
Quick fix: if you just use 2 keypads, you could configure HID macros to send a key press combination instead of launching a script, for example: keypad1num1:Ctrl-Shift-F1, keypad1num2:Ctrl-Shift-F2, keypad2num1:Alt-Shift-F1. Then have AHK intercept those keys and handle them: ^+F1::MsgBox Keypad 1, NumPad 1.

An actual fix for the script I need to think about.


You should also keep in mind the way HID macros works: I believe it detects the key press and after that it asks Windows from which keyboard the last key press came from. So if key presses are too close together (~millisecond) even HID macros can't help you, as the second press occurred before it could ask Windows.

RedBoxster
  • Members
  • 1 posts
  • Last active: Jul 03 2011 08:29 AM
  • Joined: 01 Jul 2011
Great Post by XXO but I cant seems to get things working.

I have a USB KeyPad that I wish to use for my shortcuts. When I setup all the files as suggested nothing happens... I presume I need to setup the shortcuts somewhere - is this in the HIDmacros_send.ahk??

Can someone give me some simple examples please?

Also the final post states entering into HID Macros 'send keyboard sequence" will work. When I do this the sequence is sent but rather than running the AutoHotKey shortcut it just displays the sequence...

Sorry if these are obvious question but hopefully one of the members can help...

Cheers,

Marcus

XX0
  • Members
  • 32 posts
  • Last active: Dec 12 2014 01:05 PM
  • Joined: 17 Jul 2010
The HIDmacros_KeypadScriptGenerator.ahk was written for HidMacros 1.6, which uses a ini-style configuration file. Version 2 uses an xml-style. I've updated it to use the version 2 markup but haven't tested it. So let me know if it works. When you download HidMacros 2 it doesn't have a configuration file yet, so you need to run it and configure some trivial hotkey and close it so it creates a hidmacros.xml file.
Then run the ScriptGenerator included below and paste the contents of the generated configuration into hidmacros.xml, after the <Macros> tag.

HIDmacros_KeypadScriptGenerator2.ahk
;Set variables:
         VK96=0
         VK97=1
         VK98=2
         VK99=3
         VK100=4
         VK101=5
         VK102=6
         VK103=7
         VK104=8
         VK105=9
         VK13=Enter
         VK8=BackSpace
         VK106=`*
         VK107=`+
         VK109=`-
         VK111=`/
         VK110=`.
         VK33=PgUp
         VK34=PgDn
         VK35=End
         VK36=Home
         VK37=Left
         VK38=Up
         VK39=Right
         VK40=Down
         VK45=Insert
         VK46=Delete
         VK144=NumLock
         configuration=
         keycodes:="8 13 96 97 98 99 100 101 102 103 104 105 106 107 109 110 111" ;NumLock On virtual key codes
         ;keycodes.=" 144" ;NumLock, does not work on my PC

Loop{
   InputBox, keypad, Keypad name, Enter the name of keypad #%A_Index%.`nLeave blank to continue.
   If (keypad=""){
      keypad:=A_Index-1
      Break
   }
   keypad%A_Index%:=keypad
   If (ErrorLevel)
      ExitApp
}

InputBox, exe, AutoHotkey executable location, Enter the location of the AutoHotkey executable file,,,,,,,, C:\Program Files (x86)\AutoHotkey\AutoHotkey.exe
If (ErrorLevel)
   ExitApp
   
InputBox, script, AutoHotkey script location, Enter the location of the HIDmacros_send.ahk file,,,,,,,, C:\Program Files (x86)\AutoHotkey\Domotica\HIDmacros_send.ahk
If (ErrorLevel)
   ExitApp

MsgBox, 291, NumLock off state, Should numlock off keys be included?
IfMsgBox Yes
   keycodes.=" 33 34 35 36 37 38 39 40 45 46" ;NumLock Off virtual key codes
Else IfMsgBox Cancel
   ExitApp
   
Loop,%keypad%
{
   keypadname:=keypad%A_Index%
   keypadnumber:=A_Index
   
   Loop, Parse, keycodes, %A_Space%
   {
      keyname:=VK%A_LoopField%
      configuration.="    <Macro>`r`n"
      configuration.="      <Device>" . keypadname . "</Device>`r`n"
      configuration.="      <Name>" . keypadname . ": " . keyname . "</Name>`r`n"
      configuration.="      <KeyCode>" . A_LoopField . "</KeyCode>`r`n"
      configuration.="      <Direction>down</Direction>`r`n"
      configuration.="      <Action>CMD</Action>`r`n"
      configuration.="      <Sequence></Sequence>`r`n"
      configuration.="      <SCEvent></SCEvent>`r`n"
      configuration.="      <XPLCommand></XPLCommand>`r`n"
      configuration.="      <ScriptSource></ScriptSource>`r`n"
      configuration.="      <SCText>0</SCText>`r`n"
      configuration.="      <SCParams></SCParams>`r`n"
      configuration.="      <Command>""" . exe . """ """ . script . """ " . keypadnumber . " " . A_LoopField . "</Command>`r`n"
      configuration.="    </Macro>`r`n"
   }   
}

Clipboard:=configuration
MsgBox, 64, Configuration completed, The HidMacros key configuration has been copied to the clipboard, 5


Scoox
  • Members
  • 194 posts
  • Last active: Jun 09 2017 03:34 AM
  • Joined: 28 Nov 2010
Is GlovePIE dead? I get this when I try to access the website:

Forbidden

You don't have permission to access /glovepie.php on this server.

Additionally, a 403 Forbidden error was encountered while trying to use an ErrorDocument to handle the request.


By the way, can anyone point out the main 3 or 4 differences between AHK and GlovePIE?

gwarble
  • Members
  • 624 posts
  • Last active: Aug 12 2016 07:49 PM
  • Joined: 23 May 2009
very nice script and write-up... the best solution i can think of would be to wrap the keyboard hook dll itself but thats a big job... or even better would be to write a keyboard hook dll (for system-wide application level hooking the only solution is a dll to inject in other apps process) in autohotkey_H.dll for a pure ahk solution!

any other hook or hotkey solution (that exists, not only available in ahk) will, by design, override any attempt to get the wm_input messages while other apps have focus (ie when ridev_inputsink is used, the whole goal of this age-old dilemma)... so system-wide hook is the solution... and crafty programming to let all those instances ofthe dll communicate with the main exe/script to determine the raw device that most recently sent the key command, and which ones to block... anyway maybe this is better for the AHKHID thread...


to expand on your solution, i would recommend:
use HID Macros to block the keys you want to be blocked, and use ahk for the rest (except maybe direct key replacements)
your xml builder only has to build
<Device>Kbd1</Device>
      <Name>q</Name>
      <KeyCode>81</KeyCode>
      <Direction>down</Direction>
      <Action>SEQ</Action>
      <Sequence></Sequence>
      <SCEvent></SCEvent>
      <XPLCommand></XPLCommand>
      <ScriptSource></ScriptSource>
      <SCText>0</SCText>
      <SCParams></SCParams>
      <Command></Command>
for each key
then use WM_Input to detect the RawInputData and activate script subroutines or functions based on that call... so your WM_Input handler would be the thing activating scripted routines, etc, all from one thread


a common source of confusion when debugging and trying to make this work is that the key being release causes some messages to be sent as well, but not a key delivered to your focused program... so it might appear that a regular hotkey or an ahk hotkey (which is a low level hotkey and will also block your wm_input data gathering if it eats the keystroke) is blocking your keystroke but you're still getting WM_input data, in reality you're not, not until the key is released that is (which is terrible for typing... don't bother)

anyway, very nice work... thanks for sharing
EDIT: d'oh, i guess i can't read dates on the new forum skin... thought this was a new thread
- gwarble