Popular Post Nine Posted February 14, 2021 Popular Post Posted February 14, 2021 (edited) This is based on this topic. With this UDF, you can define all the HotKeySet you want on any of the keyboards. The keys registered as HotKeySet are not sent to the active window. I also created a "Event-driven" mode for lengthy procedure. There is still the immediate action mode for short procedure. Note that in Event-driven mode, you will need to create an close loop to intercept the HotKey events. Events are asynchronous. In Event-driven mode only one procedure can be running at a given time. But immediate action procedure can interrupt a Event-driven procedure. All procedures are declared as actual function (without quotes). It is important that lengthy procedure should be declared as Event-driven, otherwise the system may become unstable. Version 2024-12-12 * Corrected bug in _MKHKS_UnRegister_HotKey * Added Restart Example in zip file Version 2024-01-04 * Added support to {LEFT} and {RIGHT} keys * Added more information about keyboards in list * Added new parameter to _MKHKS_Initialize to exclude a specific keyboard from the list. To do so, you simply provide partial part (as a string) of the device name. Version 2024-01-03 * Added support to Send command, Hotkey must be set for the first keyboard. Version 2022-12-22 * Added support to register the same Hotkey on multiple keyboards (same or different functions) * Added possibility to unregister a Hotkey for a particular keyboard * Added validation on registering the same Hotkey twice It may happen that the list would provide more keyboards than they are actually connected. To exclude one particular keyboard, you could run the following example and test the hotkeys (0, 1, 2, ...) on each keyboard to see which ones are responding. Then you can provide partial string of the device to be excluded with the _MKHKS_Initialize function. #include "MKHKS-UDF.au3" Example() Func Example() HotKeySet("{END}", _Exit) Local $iNumKB = _MKHKS_Initialize() For $i = 0 To $iNumKB - 1 _MKHKS_Register_HotKey(String($i), _Test, $i, False) ; set hotkey to keyboard number (0, 1, 2,...) Next While Sleep(50) WEnd EndFunc ;==>Example Func _Exit() Exit EndFunc ;==>_Exit Func _Test() ConsoleWrite("Test succeeded - " & @HotKeyPressed & " was Key Pressed" & @CRLF) EndFunc ;==>_Test As usual if you have any suggestion, I would be very interested to hear them. MKHKS-UDF.zip Edited December 12 by Nine argumentum, Bilgus, Lepes and 5 others 6 2 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
Lepes Posted December 21, 2022 Posted December 21, 2022 (edited) Hi! Thank you for this UDF, I love It! It seems you can not setup the same key on both keyboards at the same time, Is it intended? If so, don't read anymore, thanks anyway. EDIT: I realized that it internally uses HotkeySet, so the same hotkey couldn't been registered two timese for different keyboards. I'm sorry. If I setup {f1} hotkey for keyboard 1 and {f2} for keyboard 2, everything works perfectly, so: - both keyboards works and they are detected - There are no error registerings hotkeys. - I get Console Messages for both hotkeys press If I setup {f1} for both keyboards: - It works for keyboard 1, I get Console Messages whenever I press {f1} on keyboard 1 - It doesn't work for keyboard 2, I don't get Console Message on this hotkey and keyboard 2 This code reproduce the problem: #include <MKHKS-UDF.au3> #include <Misc.au3> _MKHKS_Initialize() _MKHKS_Register_HotKey("{f1}", _keyb1, 1 ) If @error Then ConsoleWrite("error " & @error & @CRLF) _MKHKS_Register_HotKey("{f1}", _keyb2, 2 ) If @error Then ConsoleWrite("error " & @error & @CRLF) While 1 sleep(50) _MKHKS_WaitForEvent() If _IsPressed("1B") Then ; Esc Exit EndIf Wend Func _keyb1() ConsoleWrite("pressed F1 on keyb index 1"& @CRLF) EndFunc Func _keyb2() ConsoleWrite("pressed F1 on keyb index 2 Dactyl manuform"& @CRLF) EndFunc It seems __MKHKS_FindKey function doesn't take in mind at what keyboard is registered that searched hotkey, so it finds the first one that was for Keyboard 1, and then, since The hDevice is not the same, it uses ControlSend. Func __MKHKS_WM_INPUT: Local $iHotKey = __MKHKS_FindKey($sMKHKS_HotKey) If ___MKHKS_FindKeyBoard($tRIH.hDevice) = $aMKHKS_HotKeySet[$iHotKey][2] Then If $aMKHKS_HotKeySet[$iHotKey][3] Then $iMKHKS_HotKeyPressed = $iHotKey + 1 Else $aMKHKS_HotKeySet[$iHotKey][1]() EndIf Else ControlSend("", "", "", $sMKHKS_HotKey) EndIf I Think __MKHKS_FindKey needs a second parameter (keyboard hDevice) so it only search the hotkey on the desired Keyboard hDevice. Am I right? Edited December 21, 2022 by Lepes
Nine Posted December 21, 2022 Author Posted December 21, 2022 @Lepes Glad you like my UDF. As of today I would consider it a bug, since it registers both keyboards for the same key (F1 in your example). Now I believe it would be quite easy to implement your suggestion. Only drawback is that you would need to specify a keyboard in _MKHKS_UnRegister_HotKey (I could allow no specification and all keyboards would be unregistered). Just curious what the others think about your idea. Meanwhile, I'll think of the best solution (either a bug or an enhancement). Thanks anyway for the report. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
Nine Posted December 22, 2022 Author Posted December 22, 2022 New version available Lepes 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
Lepes Posted December 23, 2022 Posted December 23, 2022 Thank you very much!! I really appreciate your time and effort. "Everyone said" that this was imposible, because Windows USB keyboard Management is transparent and you can not identify what keypress belong to a specific hardware. I have been trying to achieve this for almost a year now, since I 3DPrinted and built a handwired Dactyl manuform 4x6. I am not sure if you realize what you have done here: To achieve this I need to craft a new keyboard (buy switches, keycaps, diodes, weld everything) configure QMK firmware to assign differents functions to each keys and use a dedicated Arduino Pro-Micro for this secondary macro keyboard. Once I did everything, I got two problems: - You can not create new virtual key codes. You are sticked to remap existings ones, the only availables are from F13 to F24 - Since the firmware is inside your macro keyboard, it knows nothing about Windows OS. I don't want to change keyboard/mouse layers manually everytime I change from Mozilla Firefox to Blender, to FreeCommander and so on. I want AutoIt detects the active window and remaps everything on the fly. This UDF allows to connect any cheap keyboard and use it as a control panel, it is not just a keyboard anymore (although you can), It is a custom macro keyboard, you can program any key to do whatever you want. The best part is that your main keyboard is not affected. I mean, if you map F5 for an Autoit function, you can not press F5 anymore to update mozilla webpage. You need to pause your AutoIt script to use F5 native function. Now you have two different F5 keys, Problem solved!! It is exactly the same if you have a programmable mouse buttons and the mouse software doesn't fit your needs. I map Mouse Buttons to fixed functions keys (F1..F12) and then AutoIt remaps on the fly based on the active window, but it also remap my main keyboard, something I didn't like. ;File Generated with OdsToArray.au3 from 'Mapeado.ods' file #Region ; $aTable Global Const $WIN = 0 , $HOT = 1 , $SEN = 2 , $FUN = 3 , $HEL = 4 ; COLS constants ;Wintitle , HotKeySet , ControlSend , Function , Help Global $aTable[][] = [ _ ["Mozilla" , "{f1}" , "^+{TAB}" , "" , "prior tab" ], _ ["Mozilla" , "{f2}" , "^{TAB}" , "" , "next tab" ], _ ["Mozilla" , "{f3}" , "^w" , "" , "close tab" ], _ ["Mozilla" , "{f4}" , "^c" , "" , "copy text" ], _ ["Mozilla" , "{f5}" , "^x" , "" , "cut text" ], _ ["Mozilla" , "{f6}" , "^v" , "" , "paste text" ], _ ["Mozilla" , "+{f4}" , "" , "copyWebLink" , "copy Web link" ], _ ["Mozilla" , "!{f4}" , "" , "addWebLink" , "Add new Web link" ], _ ["Mozilla" , "{f7}" , "" , "TranslateWord" , "translate from English" ], _ ["Mozilla" , "{f10}" , "" , "GoogleText" , "" ], _ ["Blender" , "{f1}" , "+s" , "" , "Cursor a selección" ], _ ["Blender" , "{f2}" , "{NUMPADDIV}" , "" , "vista Local/Global" ], _ ["Blender" , "{f3}" , "h" , "" , "Ocultar" ], _ ["Blender" , "{f4}" , "{NUMPAD1}" , "" , "vista frontal" ], _ ["Blender" , "^{f4}" , "^{NUMPAD1}" , "" , "vista trasera" ], _ ["Blender" , "{f5}" , "{NUMPAD3}" , "" , "vista derecha" ], _ ["Blender" , "^{f5}" , "{NUMPAD3}" , "" , "vista izquierda" ], _ ["Blender" , "{f6}" , "{NUMPAD7}" , "" , "vista superior" ], _ ["Blender" , "^{f6}" , "^{NUMPAD7}" , "" , "vista inferior" ], _ ["Blender" , "{f10}" , "{NUMPADDOT}" , "" , "Focalizar selección" ], _ ["Blender" , "{left}" , "1" , "" , "Vertices" ], _ ["Blender" , "{Down}" , "2" , "" , "Aristas" ], _ ["Blender" , "{Right}" , "3" , "" , "Caras" ], _ This UDF is a really good gift, Thank you and Merry Christmas!! Nine 1
Nine Posted January 3 Author Posted January 3 New version available. Added support to Send command. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
ioa747 Posted January 3 Posted January 3 I'm having a problem here, while it shows that it catches the arrows, but it doesn't fire the function, unless I press another key #include "MKHKS-UDF.au3" ;https://www.autoitscript.com/forum/topic/205141-multiple-keyboards-hotkeyset-udf Example() ;---------------------------------------------------------------------------------------- Func Example() _MKHKS_Initialize(False) ;With True Show list of keyboards _MKHKS_Register_HotKey("{LEFT}", goLEFT, 0) ;set hotkey to 1st keyboarb _MKHKS_Register_HotKey("{RIGHT}", goRIGHT, 0) ;set hotkey to 1st keyboarb While True _MKHKS_WaitForEvent() Sleep(50) WEnd EndFunc ;==>Example ;---------------------------------------------------------------------------------------- Func goLEFT() ConsoleWrite("goLEFT" & @CRLF) EndFunc ;---------------------------------------------------------------------------------------- Func goRIGHT() ConsoleWrite("goRIGHT" & @CRLF) EndFunc I know that I know nothing
Nine Posted January 3 Author Posted January 3 @ioa747 Thanks for reporting. I got same problem as you. As of now, it seems there is a special treatment by Windows for those 2 keys. I will need to investigate further although I still do not understand why Windows would do such a thing. ioa747 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
Nine Posted January 4 Author Posted January 4 New version available. Solved previous issue and added support to exclude one particular keyboard. argumentum 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
easyL Posted December 10 Posted December 10 Thank you for this amazing UDF, Nine! A small bug that caught me today - _MKHKS_UnRegister_HotKey fails if a hotkey was registered with more than one keyboard. This is because your for loop is missing "Step -1" so that the loop is specified to go from 1 to 0, for example, which causes it to never run. The original line is: Func _MKHKS_UnRegister_HotKey ..... For $i = UBound($aIdx) - 1 To 0 If $iKeyBoard < 0 Or $aMKHKS_HotKeySet[$aIdx[$i]][2] = $iKeyBoard Then It should read: For $i = UBound($aIdx) - 1 To 0 Step -1 If $iKeyBoard < 0 Or $aMKHKS_HotKeySet[$aIdx[$i]][2] = $iKeyBoard Then On a related note, it would be nice to have a function to handle a new "keyboard" being attached. The problem is the merely re-assigning $aMKHKS_Keyboard, aside from messing with an "internal variable" is not sufficient, since it then breaks any currently assigned hotkeys. Preserving hotkeys across a change of the keyboard list would be a bit tricky -- you currently don't store enough information in $aMKHKS_HotKeySet to do it. But at the very least, all existing hotkeys should be unregistered. My proposed function would be something like Func RescanKeyboards($exclude) Local $aCurrentKbds = __MKHKS_EnumRawKeyboards($exclude) If _ArrayToString($aMKHKS_Keyboard) <> _ArrayToString($aCurrentKbds) Then "ToDo: Unregister all hotkeys" $aMKHKS_Keyboard = $aCurrentKbds Return True ;kdbs changed, user needs to reinitialize all hotkeys Endif Return False EndFunc My use case: I'm handling "keystrokes" from a remote control (Logitech R400 Wireless Presenter Remote Control), but also assigns hotkeys to the local keyboard. The script runs at Windows startup but the usb stick for the remote is not immediately plugged in. So if the code doesn't find the remote (based on its ID), it scans until it is found, then updates the keyboard definitions. Also, to make the program a bit less dependent on exact ID strings, anything that is not the remote controller is treated as the local keyboard -- usually there is more than one of these, just because of how the manufacturers specify their keyboards (keypad vs. main kbd, for example), hence the need to unregister keys from multiple keyboards. Thanks again for all your hard work on this! Nine 1
Nine Posted December 10 Author Posted December 10 (edited) @easyL Thanks for reporting the bug in _MKHKS_UnRegister_HotKey. I will make a new version soon. Instead of creating a new function to rescan and unregister all hotkeys, I suggest you use this script that will restart on the addition of a new keyboard : expandcollapse popup#include "C:\Apps\AutoIt\Keyboard Mouse\MKHKS-UDF.au3" HotKeySet("{ESC}", Terminate) HotKeySet("{HOME}", Restart) ; manual restart If $CmdLine[0] Then Sleep(500) ; give time for restart, so all hotkeys from previous start are unset Local $iNumKB = AssignHotKeys() While Sleep(100) _MKHKS_WaitForEvent() If $iNumKB <> UBound(__MKHKS_EnumRawKeyboards("194a8f")) Then Restart() ; automatic restart WEnd Func Restart() Exit ShellExecute(@ScriptFullPath, "Restart") EndFunc ;==>Restart Func Terminate() Exit EndFunc ;==>Terminate Func AssignHotKeys() Local $iNum = _MKHKS_Initialize(False, "194a8f") Switch $iNum Case 2 ; register hotkeys on secondary KB when available _MKHKS_Register_HotKey("{F1}", DoF1, 1, False) ContinueCase Case 1 ; register hotkeys on local KB _MKHKS_Register_HotKey("{F2}", DoF2, 0, True) EndSwitch Return $iNum EndFunc ;==>AssignHotKeys Func DoF1() MsgBox($MB_OK, "", "F1") EndFunc ;==>DoF1 Func DoF2() MsgBox($MB_OK, "", "F2") EndFunc ;==>DoF2 Let me know if that should work for you. I prefer this approach because it will close all current execution of hotkeys and restart a clean instance. Edited December 10 by Nine suggested solution “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
Nine Posted December 12 Author Posted December 12 New version available. ioa747 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy
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