├── .gitattributes ├── MacroKeyboard.rar ├── MacroKeyboard.res ├── Resources ├── Icon.ico ├── Icon.png ├── Screen3.png ├── screen1.png └── screen2.png ├── MacroKeyboard_Icon.ico ├── MacroKeyboardComponents.res ├── README.md ├── MacroKeyboard.dpr ├── MacroKeyboardComponents.dpk ├── .gitignore ├── Units ├── HID.Constants.pas ├── untSettings.pas ├── HID.MacroKeyboard.Hotkey.pas ├── untSettings.dfm ├── HID.Types.pas ├── untKeyMacro.pas ├── untKnobMacro.pas ├── untKnobMacro.dfm ├── untKeyMacro.dfm ├── HID.MacroKeyboard.pas ├── HID.MacroKeyboard.Config.pas └── HID.pas └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /MacroKeyboard.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/MacroKeyboard.rar -------------------------------------------------------------------------------- /MacroKeyboard.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/MacroKeyboard.res -------------------------------------------------------------------------------- /Resources/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/Resources/Icon.ico -------------------------------------------------------------------------------- /Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/Resources/Icon.png -------------------------------------------------------------------------------- /MacroKeyboard_Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/MacroKeyboard_Icon.ico -------------------------------------------------------------------------------- /Resources/Screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/Resources/Screen3.png -------------------------------------------------------------------------------- /Resources/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/Resources/screen1.png -------------------------------------------------------------------------------- /Resources/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/Resources/screen2.png -------------------------------------------------------------------------------- /MacroKeyboardComponents.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erdesigns-eu/HID-Macro-Keyboard/HEAD/MacroKeyboardComponents.res -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Custom software for a Maccro Keyboard with 12 buttons and 3 Knobs bought on Amazon. 2 | 3 | Known bugs: 4 | - Media "Mute", "Play/Pause" are not working. I need the original software to be able to compare the commands.. 5 | 6 | This is a first release, so there may be bugs that im not aware of. If you want to help, please submit a pull request. 7 | Download link: https://github.com/erdesigns-eu/HID-Macro-Keyboard/blob/main/MacroKeyboard.rar 8 | 9 | The software should be self explainatory, you can click a button/knob to select it and use the menu to clear or assign a macro, or you can use the context menu (right click). You can enable/disable some things in the settings, for now there is no installer because this is jus the first BETA release. After the first bugs will be fixed i will create a installer and maybe add some more functions, documentation, etc. 10 | 11 | ![Assign a macro to a key](Resources/screen1.png) 12 | ![Context menu](Resources/screen2.png) 13 | ![Settings](Resources/Screen3.png) 14 | -------------------------------------------------------------------------------- /MacroKeyboard.dpr: -------------------------------------------------------------------------------- 1 | program MacroKeyboard; 2 | 3 | uses 4 | Vcl.Forms, 5 | untMain in 'Units\untMain.pas' {frmMain}, 6 | HID.Constants in 'Units\HID.Constants.pas', 7 | HID.Types in 'Units\HID.Types.pas', 8 | HID in 'Units\HID.pas', 9 | HID.MacroKeyboard in 'Units\HID.MacroKeyboard.pas', 10 | HID.MacroKeyboard.Component in 'Units\HID.MacroKeyboard.Component.pas', 11 | Vcl.Themes, 12 | Vcl.Styles, 13 | HID.MacroKeyboard.Config in 'Units\HID.MacroKeyboard.Config.pas', 14 | untKeyMacro in 'Units\untKeyMacro.pas' {frmKeyMacro}, 15 | untKnobMacro in 'Units\untKnobMacro.pas' {frmKnobMacro}, 16 | untSettings in 'Units\untSettings.pas' {frmSettings}, 17 | HID.MacroKeyboard.Hotkey in 'Units\HID.MacroKeyboard.Hotkey.pas'; 18 | 19 | {$R *.res} 20 | 21 | begin 22 | Application.Initialize; 23 | Application.MainFormOnTaskbar := True; 24 | Application.CreateForm(TfrmMain, frmMain); 25 | Application.CreateForm(TfrmKeyMacro, frmKeyMacro); 26 | Application.CreateForm(TfrmKnobMacro, frmKnobMacro); 27 | Application.CreateForm(TfrmSettings, frmSettings); 28 | Application.Run; 29 | end. 30 | -------------------------------------------------------------------------------- /MacroKeyboardComponents.dpk: -------------------------------------------------------------------------------- 1 | package MacroKeyboardComponents; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$IMPLICITBUILD ON} 29 | 30 | requires 31 | rtl, 32 | vcl, 33 | RESTComponents; 34 | 35 | contains 36 | HID.MacroKeyboard.Component in 'Units\HID.MacroKeyboard.Component.pas', 37 | HID.MacroKeyboard.Config in 'Units\HID.MacroKeyboard.Config.pas', 38 | HID.MacroKeyboard.Hotkey in 'Units\HID.MacroKeyboard.Hotkey.pas', 39 | HID.Constants in 'Units\HID.Constants.pas', 40 | HID in 'Units\HID.pas', 41 | HID.Types in 'Units\HID.Types.pas', 42 | HID.MacroKeyboard in 'Units\HID.MacroKeyboard.pas'; 43 | 44 | end. 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss 69 | modules/ 70 | -------------------------------------------------------------------------------- /Units/HID.Constants.pas: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // UNIT : HID.Constants.pas 3 | // CONTENTS : HID Device related constants 4 | // VERSION : 1.0 5 | // TARGET : Embarcadero Delphi 11 or higher 6 | // AUTHOR : Ernst Reidinga (ERDesigns) 7 | // STATUS : Open source under Apache 2.0 library 8 | // COMPATIBILITY : Windows 7, 8/8.1, 10, 11 9 | // RELEASE DATE : 19/05/2024 10 | //------------------------------------------------------------------------------ 11 | unit HID.Constants; 12 | 13 | interface 14 | 15 | const 16 | /// 17 | /// SetupAPI DLL Name. 18 | /// 19 | SETUPAPI_DLL = 'setupapi.dll'; 20 | 21 | const 22 | /// 23 | /// HID DLL Name. 24 | /// 25 | HID_DLL = 'hid.dll'; 26 | 27 | const 28 | /// 29 | /// Macro data length in Bytes. (HID report structure) 30 | /// Byte 0 = Report ID, Byte 1 - 64 = Data 31 | /// 32 | MACRO_DATA_LENGTH = 65; 33 | 34 | const 35 | /// 36 | /// Retrieve only devices that are currently present. 37 | /// 38 | DIGCF_PRESENT = $00000002; 39 | /// 40 | /// Retrieve devices that expose the specified device interface. 41 | /// 42 | DIGCF_DEVICEINTERFACE = $00000010; 43 | 44 | const 45 | /// 46 | /// GUID for HID device interfaces. 47 | /// 48 | GUID_DEVINTERFACE_HID: TGUID = '{4D1E55B2-F16F-11CF-88CB-001111000030}'; 49 | 50 | const 51 | /// 52 | /// Device description property key. 53 | /// 54 | SPDRP_DEVICEDESC = $00000000; 55 | /// 56 | /// Hardware ID property key. 57 | /// 58 | SPDRP_HARDWAREID = $00000001; 59 | /// 60 | /// Compatible IDs property key. 61 | /// 62 | SPDRP_COMPATIBLEIDS = $00000002; 63 | /// 64 | /// Service name property key. 65 | /// 66 | SPDRP_SERVICE = $00000004; 67 | /// 68 | /// Device class property key. 69 | /// 70 | SPDRP_CLASS = $00000007; 71 | /// 72 | /// Class GUID property key. 73 | /// 74 | SPDRP_CLASSGUID = $00000008; 75 | /// 76 | /// Driver key property key. 77 | /// 78 | SPDRP_DRIVER = $00000009; 79 | /// 80 | /// Manufacturer name property key. 81 | /// 82 | SPDRP_MFG = $0000000B; 83 | /// 84 | /// Friendly name property key. 85 | /// 86 | SPDRP_FRIENDLYNAME = $0000000C; 87 | /// 88 | /// Location information property key. 89 | /// 90 | SPDRP_LOCATION_INFORMATION = $0000000D; 91 | /// 92 | /// Physical device object name property key. 93 | /// 94 | SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0000000E; 95 | /// 96 | /// Capabilities property key. 97 | /// 98 | SPDRP_CAPABILITIES = $0000000F; 99 | /// 100 | /// UI number property key. 101 | /// 102 | SPDRP_UINUMBER = $00000010; 103 | /// 104 | /// Indicates successful HIDP operation. 105 | /// 106 | HIDP_STATUS_SUCCESS = $00110000; 107 | 108 | const 109 | /// 110 | /// The GUID for USB device interface class notifications. 111 | /// 112 | GUID_DEVINTERFACE_USB_DEVICE: TGUID = '{A5DCBF10-6530-11D2-901F-00C04FB951ED}'; 113 | /// 114 | /// System detected a new device. 115 | /// 116 | DBT_DEVICEARRIVAL = $8000; 117 | /// 118 | /// Device is gone. 119 | /// 120 | DBT_DEVICEREMOVECOMPLETE = $8004; 121 | /// 122 | /// Device interface class. Used with DEV_BROADCAST_DEVICEINTERFACE. 123 | /// 124 | DBT_DEVTYP_DEVICEINTERFACE = $00000005; 125 | 126 | const 127 | /// 128 | /// HID Manufacturer string. 129 | /// 130 | HID_MANUFACTURER_STRING = 1; 131 | /// 132 | /// HID Product string. 133 | /// 134 | HID_PRODUCT_STRING = 2; 135 | /// 136 | /// HID Serial Code string. 137 | /// 138 | HID_SERIALCODE_STRING = 3; 139 | 140 | implementation 141 | 142 | end. 143 | -------------------------------------------------------------------------------- /Units/untSettings.pas: -------------------------------------------------------------------------------- 1 | unit untSettings; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, System.Win.Registry; 8 | 9 | type 10 | TfrmSettings = class(TForm) 11 | pnlBottom: TPanel; 12 | btnOK: TButton; 13 | btnCancel: TButton; 14 | gbView: TGroupBox; 15 | cbZoomOnScroll: TCheckBox; 16 | GroupBox1: TGroupBox; 17 | cbLoadLastOpenedConfigOnStart: TCheckBox; 18 | cbRememberZoomLevel: TCheckBox; 19 | GroupBox2: TGroupBox; 20 | cbShowKeyKnobHint: TCheckBox; 21 | GroupBox3: TGroupBox; 22 | cbTrayIconVisible: TCheckBox; 23 | cbMinimizeToTray: TCheckBox; 24 | cbSetMacroKeysOnOpenConfig: TCheckBox; 25 | GroupBox4: TGroupBox; 26 | cbRememberWindowPosition: TCheckBox; 27 | cbRememberWindowState: TCheckBox; 28 | public 29 | /// 30 | /// Load Settings from Registry 31 | /// 32 | procedure LoadSettings; 33 | /// 34 | /// Save Settings to Registry 35 | /// 36 | procedure SaveSettings; 37 | /// 38 | /// Execute settings dialog 39 | /// 40 | function Execute: TModalResult; 41 | end; 42 | 43 | var 44 | frmSettings: TfrmSettings; 45 | 46 | const 47 | /// 48 | /// Registry Root Key 49 | /// 50 | RootKey: HKEY = HKEY_CURRENT_USER; 51 | /// 52 | /// Registry Key 53 | /// 54 | RegKey: string = '\Software\ERDesigns\Macro Keyboard\'; 55 | 56 | implementation 57 | 58 | {$R *.dfm} 59 | 60 | //------------------------------------------------------------------------------ 61 | // READ REGISTRY BOOL DEFAULT 62 | //------------------------------------------------------------------------------ 63 | function ReadBoolDefault(const Reg: TRegistry; const Name: string; const Default: Boolean): Boolean; 64 | begin 65 | try 66 | if Reg.ValueExists(Name) then 67 | Result := Reg.ReadBool(Name) 68 | else 69 | Result := Default; 70 | except 71 | Result := Default; 72 | end; 73 | end; 74 | 75 | //------------------------------------------------------------------------------ 76 | // LOAD SETTINGS 77 | //------------------------------------------------------------------------------ 78 | procedure TfrmSettings.LoadSettings; 79 | var 80 | Reg: TRegistry; 81 | begin 82 | Reg := TRegistry.Create; 83 | try 84 | // Set root key 85 | Reg.RootKey := RootKey; 86 | // Open registry key 87 | if Reg.OpenKeyReadOnly(RegKey) then 88 | begin 89 | // Zoom 90 | cbZoomOnScroll.Checked := ReadBoolDefault(Reg, 'ZoomOnScroll', True); 91 | cbRememberZoomLevel.Checked := ReadBoolDefault(Reg, 'RememberZoomLevel', True); 92 | // Hints 93 | cbShowKeyKnobHint.Checked := ReadBoolDefault(Reg, 'ShowKeyKnobHint', True); 94 | // Tray Icon 95 | cbTrayIconVisible.Checked := ReadBoolDefault(Reg, 'TrayIconVisible', True); 96 | cbMinimizeToTray.Checked := ReadBoolDefault(Reg, 'MinimizeToTray', True); 97 | // Config 98 | cbLoadLastOpenedConfigOnStart.Checked := ReadBoolDefault(Reg, 'LoadLastOpenedConfigOnStart', True); 99 | cbSetMacroKeysOnOpenConfig.Checked := ReadBoolDefault(Reg, 'SetMacroKeysOnOpenConfig', True); 100 | // Window 101 | cbRememberWindowPosition.Checked := ReadBoolDefault(Reg, 'RememberWindowPosition', True); 102 | cbRememberWindowState.Checked := ReadBoolDefault(Reg, 'RememberWindowState', True); 103 | end else 104 | begin 105 | // Zoom 106 | cbZoomOnScroll.Checked := True; 107 | cbRememberZoomLevel.Checked := True; 108 | // Hints 109 | cbShowKeyKnobHint.Checked := True; 110 | // Tray Icon 111 | cbTrayIconVisible.Checked := True; 112 | cbMinimizeToTray.Checked := True; 113 | // Config 114 | cbLoadLastOpenedConfigOnStart.Checked := True; 115 | cbSetMacroKeysOnOpenConfig.Checked := True; 116 | // Window 117 | cbRememberWindowPosition.Checked := True; 118 | cbRememberWindowState.Checked := True; 119 | end; 120 | finally 121 | Reg.Free; 122 | end; 123 | end; 124 | 125 | //------------------------------------------------------------------------------ 126 | // SAVE SETTINGS 127 | //------------------------------------------------------------------------------ 128 | procedure TfrmSettings.SaveSettings; 129 | var 130 | Reg: TRegistry; 131 | begin 132 | Reg := TRegistry.Create; 133 | try 134 | // Set root key 135 | Reg.RootKey := RootKey; 136 | // Open registry key 137 | if Reg.OpenKey(RegKey, True) then 138 | begin 139 | // Zoom 140 | Reg.WriteBool('ZoomOnScroll', cbZoomOnScroll.Checked); 141 | Reg.WriteBool('RememberZoomLevel', cbRememberZoomLevel.Checked); 142 | // Hints 143 | Reg.WriteBool('ShowKeyKnobHint', cbShowKeyKnobHint.Checked); 144 | // Tray Icon 145 | Reg.WriteBool('TrayIconVisible', cbTrayIconVisible.Checked); 146 | Reg.WriteBool('MinimizeToTray', cbMinimizeToTray.Checked); 147 | // Config 148 | Reg.WriteBool('LoadLastOpenedConfigOnStart', cbLoadLastOpenedConfigOnStart.Checked); 149 | Reg.WriteBool('SetMacroKeysOnOpenConfig', cbSetMacroKeysOnOpenConfig.Checked); 150 | // Window 151 | Reg.WriteBool('RememberWindowPosition', cbRememberWindowPosition.Checked); 152 | Reg.WriteBool('RememberWindowState', cbRememberWindowState.Checked); 153 | end; 154 | finally 155 | Reg.Free; 156 | end; 157 | end; 158 | 159 | //------------------------------------------------------------------------------ 160 | // EXECUTE DIALOG 161 | //------------------------------------------------------------------------------ 162 | function TfrmSettings.Execute: TModalResult; 163 | begin 164 | // Load settings from registry 165 | LoadSettings; 166 | // Set caption 167 | Caption := 'Settings'; 168 | // Show dialog modal 169 | Result := ShowModal; 170 | end; 171 | 172 | end. 173 | -------------------------------------------------------------------------------- /Units/HID.MacroKeyboard.Hotkey.pas: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // UNIT : HID.MacroKeyboard.Hotkey.pas 3 | // CONTENTS : HID MacroKeyboard Hotkey Edit Component 4 | // VERSION : 1.0 5 | // TARGET : Embarcadero Delphi 11 or higher 6 | // AUTHOR : Ernst Reidinga (ERDesigns) 7 | // STATUS : Open source under Apache 2.0 library 8 | // COMPATIBILITY : Windows 7, 8/8.1, 10, 11 9 | // RELEASE DATE : 26/05/2024 10 | //------------------------------------------------------------------------------ 11 | unit HID.MacroKeyboard.Hotkey; 12 | 13 | interface 14 | 15 | uses 16 | System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls, Winapi.Messages, 17 | WinApi.Windows; 18 | 19 | type 20 | TMacroKeyboardHotKey = class(TEdit) 21 | private 22 | /// 23 | /// Key Down Event Handler 24 | /// 25 | procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN; 26 | /// 27 | /// Key Up Event Handler 28 | /// 29 | procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP; 30 | /// 31 | /// WM_CHAR Handler 32 | /// 33 | procedure WMChar(var Message: TWMChar); message WM_CHAR; 34 | /// 35 | /// Convert Virtual Key Code to String 36 | /// 37 | function KeyToStr(Key: Word): string; 38 | public 39 | /// 40 | /// Constructor 41 | /// 42 | constructor Create(AOwner: TComponent); override; 43 | end; 44 | 45 | implementation 46 | 47 | //------------------------------------------------------------------------------ 48 | // CONSTRUCTOR 49 | //------------------------------------------------------------------------------ 50 | constructor TMacroKeyboardHotKey.Create(AOwner: TComponent); 51 | begin 52 | // Call inherited constructor 53 | inherited; 54 | 55 | // Set read only to true because we are setting the text ourselves 56 | ReadOnly := True; 57 | end; 58 | 59 | //------------------------------------------------------------------------------ 60 | // ON KEY DOWN 61 | //------------------------------------------------------------------------------ 62 | procedure TMacroKeyboardHotKey.WMKeyDown(var Message: TWMKeyDown); 63 | begin 64 | // Update the text with the pressed key 65 | Text := KeyToStr(Message.CharCode); 66 | // Prevent the key from being processed further 67 | Message.Result := 0; 68 | end; 69 | 70 | //------------------------------------------------------------------------------ 71 | // ON KEY UP 72 | //------------------------------------------------------------------------------ 73 | procedure TMacroKeyboardHotKey.WMKeyUp(var Message: TWMKeyUp); 74 | begin 75 | // Prevent the key from being processed further 76 | Message.Result := 0; 77 | end; 78 | 79 | //------------------------------------------------------------------------------ 80 | // WM_CHAR HANDLER 81 | //------------------------------------------------------------------------------ 82 | procedure TMacroKeyboardHotKey.WMChar(var Message: TWMChar); 83 | begin 84 | // Prevent any character input 85 | Message.Result := 0; 86 | end; 87 | 88 | //------------------------------------------------------------------------------ 89 | // KEY CODE TO STRING 90 | //------------------------------------------------------------------------------ 91 | function TMacroKeyboardHotKey.KeyToStr(Key: Word): string; 92 | begin 93 | case Key of 94 | VK_F1..VK_F24: 95 | Result := 'F' + IntToStr(Key - VK_F1 + 1); 96 | VK_HOME: 97 | Result := 'HOME'; 98 | VK_END: 99 | Result := 'END'; 100 | VK_INSERT: 101 | Result := 'INSERT'; 102 | VK_DELETE: 103 | Result := 'DELETE'; 104 | VK_UP: 105 | Result := 'UP'; 106 | VK_DOWN: 107 | Result := 'DOWN'; 108 | VK_LEFT: 109 | Result := 'LEFT'; 110 | VK_RIGHT: 111 | Result := 'RIGHT'; 112 | VK_SPACE: 113 | Result := 'SPACE'; 114 | VK_RETURN: 115 | Result := 'RETURN'; 116 | VK_ESCAPE: 117 | Result := 'ESC'; 118 | VK_BACK: 119 | Result := 'BACKSPACE'; 120 | VK_PRIOR: 121 | Result := 'PAGE UP'; 122 | VK_NEXT: 123 | Result := 'PAGE DOWN'; 124 | VK_SNAPSHOT: 125 | Result := 'PRINT SCREEN'; 126 | VK_TAB: 127 | Result := 'TAB'; 128 | VK_NUMLOCK: 129 | Result := 'NUM LOCK'; 130 | VK_SCROLL: 131 | Result := 'SCROLL LOCK'; 132 | VK_CAPITAL: 133 | Result := 'CAPS LOCK'; 134 | VK_PAUSE: 135 | Result := 'PAUSE'; 136 | 137 | VK_NUMPAD0: 138 | Result := 'NUMPAD 0'; 139 | VK_NUMPAD1: 140 | Result := 'NUMPAD 1'; 141 | VK_NUMPAD2: 142 | Result := 'NUMPAD 2'; 143 | VK_NUMPAD3: 144 | Result := 'NUMPAD 3'; 145 | VK_NUMPAD4: 146 | Result := 'NUMPAD 4'; 147 | VK_NUMPAD5: 148 | Result := 'NUMPAD 5'; 149 | VK_NUMPAD6: 150 | Result := 'NUMPAD 6'; 151 | VK_NUMPAD7: 152 | Result := 'NUMPAD 7'; 153 | VK_NUMPAD8: 154 | Result := 'NUMPAD 8'; 155 | VK_NUMPAD9: 156 | Result := 'NUMPAD 9'; 157 | VK_MULTIPLY: 158 | Result := 'NUMPAD *'; 159 | VK_ADD: 160 | Result := 'NUMPAD +'; 161 | VK_SEPARATOR: 162 | Result := 'NUMPAD ,'; 163 | VK_SUBTRACT: 164 | Result := 'NUMPAD -'; 165 | VK_DECIMAL: 166 | Result := 'NUMPAD .'; 167 | VK_DIVIDE: 168 | Result := 'NUMPAD /'; 169 | 170 | VK_OEM_COMMA: 171 | Result := ','; 172 | VK_OEM_PERIOD: 173 | Result := '.'; 174 | VK_OEM_1: 175 | Result := ';'; 176 | VK_OEM_PLUS: 177 | Result := '+'; 178 | VK_OEM_MINUS: 179 | Result := '-'; 180 | VK_OEM_2: 181 | Result := '/'; 182 | VK_OEM_3: 183 | Result := '`'; 184 | VK_OEM_4: 185 | Result := '['; 186 | VK_OEM_5: 187 | Result := '\'; 188 | VK_OEM_6: 189 | Result := ']'; 190 | VK_OEM_7: 191 | Result := ''''; 192 | VK_OEM_102: 193 | Result := '<'; 194 | 195 | else 196 | Result := Chr(Key); 197 | end; 198 | end; 199 | 200 | end. 201 | 202 | -------------------------------------------------------------------------------- /Units/untSettings.dfm: -------------------------------------------------------------------------------- 1 | object frmSettings: TfrmSettings 2 | Left = 0 3 | Top = 0 4 | BorderStyle = bsDialog 5 | Caption = 'frmSettings' 6 | ClientHeight = 432 7 | ClientWidth = 401 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -12 12 | Font.Name = 'Segoe UI' 13 | Font.Style = [] 14 | Position = poMainFormCenter 15 | TextHeight = 15 16 | object pnlBottom: TPanel 17 | Left = 0 18 | Top = 392 19 | Width = 401 20 | Height = 40 21 | Align = alBottom 22 | BevelOuter = bvNone 23 | ShowCaption = False 24 | TabOrder = 5 25 | ExplicitTop = 391 26 | ExplicitWidth = 397 27 | object btnOK: TButton 28 | AlignWithMargins = True 29 | Left = 199 30 | Top = 7 31 | Width = 93 32 | Height = 26 33 | Margins.Left = 0 34 | Margins.Top = 7 35 | Margins.Right = 8 36 | Margins.Bottom = 7 37 | Align = alRight 38 | Caption = 'OK' 39 | ModalResult = 1 40 | TabOrder = 0 41 | ExplicitLeft = 195 42 | end 43 | object btnCancel: TButton 44 | AlignWithMargins = True 45 | Left = 300 46 | Top = 7 47 | Width = 93 48 | Height = 26 49 | Margins.Left = 0 50 | Margins.Top = 7 51 | Margins.Right = 8 52 | Margins.Bottom = 7 53 | Align = alRight 54 | Caption = 'Cancel' 55 | ModalResult = 2 56 | TabOrder = 1 57 | ExplicitLeft = 296 58 | end 59 | end 60 | object gbView: TGroupBox 61 | AlignWithMargins = True 62 | Left = 8 63 | Top = 4 64 | Width = 385 65 | Height = 77 66 | Margins.Left = 8 67 | Margins.Top = 4 68 | Margins.Right = 8 69 | Margins.Bottom = 0 70 | Align = alTop 71 | Caption = ' Zoom: ' 72 | TabOrder = 0 73 | ExplicitWidth = 381 74 | DesignSize = ( 75 | 385 76 | 77) 77 | object cbZoomOnScroll: TCheckBox 78 | Left = 16 79 | Top = 24 80 | Width = 345 81 | Height = 17 82 | Anchors = [akLeft, akTop, akRight] 83 | Caption = 'Zoom in/out with scroll' 84 | TabOrder = 0 85 | ExplicitWidth = 341 86 | end 87 | object cbRememberZoomLevel: TCheckBox 88 | Left = 16 89 | Top = 47 90 | Width = 345 91 | Height = 17 92 | Anchors = [akLeft, akTop, akRight] 93 | Caption = 'Remember zoom level' 94 | TabOrder = 1 95 | ExplicitWidth = 341 96 | end 97 | end 98 | object GroupBox1: TGroupBox 99 | AlignWithMargins = True 100 | Left = 8 101 | Top = 221 102 | Width = 385 103 | Height = 76 104 | Margins.Left = 8 105 | Margins.Top = 4 106 | Margins.Right = 8 107 | Margins.Bottom = 0 108 | Align = alTop 109 | Caption = ' Config: ' 110 | TabOrder = 3 111 | ExplicitWidth = 381 112 | DesignSize = ( 113 | 385 114 | 76) 115 | object cbLoadLastOpenedConfigOnStart: TCheckBox 116 | Left = 16 117 | Top = 24 118 | Width = 345 119 | Height = 17 120 | Anchors = [akLeft, akTop, akRight] 121 | Caption = 'Load last opened config on application start' 122 | TabOrder = 0 123 | ExplicitWidth = 341 124 | end 125 | object cbSetMacroKeysOnOpenConfig: TCheckBox 126 | Left = 16 127 | Top = 47 128 | Width = 345 129 | Height = 17 130 | Anchors = [akLeft, akTop, akRight] 131 | Caption = 'Set macro keys on opening config' 132 | TabOrder = 1 133 | ExplicitWidth = 341 134 | end 135 | end 136 | object GroupBox2: TGroupBox 137 | AlignWithMargins = True 138 | Left = 8 139 | Top = 85 140 | Width = 385 141 | Height = 52 142 | Margins.Left = 8 143 | Margins.Top = 4 144 | Margins.Right = 8 145 | Margins.Bottom = 0 146 | Align = alTop 147 | Caption = ' Hints: ' 148 | TabOrder = 1 149 | ExplicitWidth = 381 150 | DesignSize = ( 151 | 385 152 | 52) 153 | object cbShowKeyKnobHint: TCheckBox 154 | Left = 16 155 | Top = 23 156 | Width = 345 157 | Height = 17 158 | Anchors = [akLeft, akTop, akRight] 159 | Caption = 'Show hint with name on Key/Knob' 160 | TabOrder = 0 161 | ExplicitWidth = 341 162 | end 163 | end 164 | object GroupBox3: TGroupBox 165 | AlignWithMargins = True 166 | Left = 8 167 | Top = 141 168 | Width = 385 169 | Height = 76 170 | Margins.Left = 8 171 | Margins.Top = 4 172 | Margins.Right = 8 173 | Margins.Bottom = 0 174 | Align = alTop 175 | Caption = ' Tray Icon: ' 176 | TabOrder = 2 177 | ExplicitWidth = 381 178 | DesignSize = ( 179 | 385 180 | 76) 181 | object cbTrayIconVisible: TCheckBox 182 | Left = 16 183 | Top = 23 184 | Width = 345 185 | Height = 17 186 | Anchors = [akLeft, akTop, akRight] 187 | Caption = 'Visible' 188 | TabOrder = 0 189 | ExplicitWidth = 341 190 | end 191 | object cbMinimizeToTray: TCheckBox 192 | Left = 16 193 | Top = 46 194 | Width = 345 195 | Height = 17 196 | Anchors = [akLeft, akTop, akRight] 197 | Caption = 'Minimize to tray' 198 | TabOrder = 1 199 | ExplicitWidth = 341 200 | end 201 | end 202 | object GroupBox4: TGroupBox 203 | AlignWithMargins = True 204 | Left = 8 205 | Top = 301 206 | Width = 385 207 | Height = 76 208 | Margins.Left = 8 209 | Margins.Top = 4 210 | Margins.Right = 8 211 | Margins.Bottom = 0 212 | Align = alTop 213 | Caption = ' Window: ' 214 | TabOrder = 4 215 | ExplicitWidth = 381 216 | DesignSize = ( 217 | 385 218 | 76) 219 | object cbRememberWindowPosition: TCheckBox 220 | Left = 16 221 | Top = 24 222 | Width = 345 223 | Height = 17 224 | Anchors = [akLeft, akTop, akRight] 225 | Caption = 'Remember position' 226 | TabOrder = 0 227 | ExplicitWidth = 341 228 | end 229 | object cbRememberWindowState: TCheckBox 230 | Left = 16 231 | Top = 47 232 | Width = 345 233 | Height = 17 234 | Anchors = [akLeft, akTop, akRight] 235 | Caption = 'Remember state' 236 | TabOrder = 1 237 | ExplicitWidth = 341 238 | end 239 | end 240 | end 241 | -------------------------------------------------------------------------------- /Units/HID.Types.pas: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // UNIT : HID.Types.pas 3 | // CONTENTS : HID Device related types 4 | // VERSION : 1.0 5 | // TARGET : Embarcadero Delphi 11 or higher 6 | // AUTHOR : Ernst Reidinga (ERDesigns) 7 | // STATUS : Open source under Apache 2.0 library 8 | // COMPATIBILITY : Windows 7, 8/8.1, 10, 11 9 | // RELEASE DATE : 19/05/2024 10 | //------------------------------------------------------------------------------ 11 | unit HID.Types; 12 | 13 | interface 14 | 15 | uses 16 | WinApi.Windows; 17 | 18 | type 19 | /// 20 | /// Handle to a device information set. 21 | /// 22 | HDEVINFO = Pointer; 23 | 24 | type 25 | /// 26 | /// Pointer to a HID preparsed data structure. 27 | /// 28 | PHIDP_PREPARSED_DATA = Pointer; 29 | 30 | type 31 | /// 32 | /// Structure containing information about a device interface. 33 | /// 34 | SP_DEVICE_INTERFACE_DATA = record 35 | /// 36 | /// Size of the structure. 37 | /// 38 | cbSize: DWORD; 39 | /// 40 | /// GUID of the device interface class. 41 | /// 42 | InterfaceClassGuid: TGUID; 43 | /// 44 | /// Flags that provide additional information about the interface. 45 | /// 46 | Flags: DWORD; 47 | /// 48 | /// Reserved for future use. 49 | /// 50 | Reserved: ULONG_PTR; 51 | end; 52 | PSP_DEVICE_INTERFACE_DATA = ^SP_DEVICE_INTERFACE_DATA; 53 | 54 | /// 55 | /// Structure containing detailed information about a device interface. 56 | /// 57 | SP_DEVICE_INTERFACE_DETAIL_DATA_W = record 58 | /// 59 | /// Size of the structure. 60 | /// 61 | cbSize: DWORD; 62 | /// 63 | /// Path to the device interface. 64 | /// 65 | DevicePath: array[0..ANYSIZE_ARRAY - 1] of WideChar; 66 | end; 67 | PSP_DEVICE_INTERFACE_DETAIL_DATA_W = ^SP_DEVICE_INTERFACE_DETAIL_DATA_W; 68 | 69 | /// 70 | /// Structure containing information about a device. 71 | /// 72 | SP_DEVINFO_DATA = record 73 | /// 74 | /// Size of the structure. 75 | /// 76 | cbSize: DWORD; 77 | /// 78 | /// GUID of the device's setup class. 79 | /// 80 | ClassGuid: TGUID; 81 | /// 82 | /// Handle to the device instance. 83 | /// 84 | DevInst: DWORD; 85 | /// 86 | /// Reserved for future use. 87 | /// 88 | Reserved: ULONG_PTR; 89 | end; 90 | PSP_DEVINFO_DATA = ^SP_DEVINFO_DATA; 91 | 92 | type 93 | /// 94 | /// Structure containing capabilities of a HID device. 95 | /// 96 | HIDP_CAPS = record 97 | /// 98 | /// Top-level collection's usage ID. 99 | /// 100 | Usage: USHORT; 101 | /// 102 | /// Top-level collection's usage page. 103 | /// 104 | UsagePage: USHORT; 105 | /// 106 | /// Size of the input report in bytes. 107 | /// 108 | InputReportByteLength: USHORT; 109 | /// 110 | /// Size of the output report in bytes. 111 | /// 112 | OutputReportByteLength: USHORT; 113 | /// 114 | /// Size of the feature report in bytes. 115 | /// 116 | FeatureReportByteLength: USHORT; 117 | /// 118 | /// Reserved for future use. 119 | /// 120 | Reserved: array[0..16] of USHORT; 121 | /// 122 | /// Number of link collection nodes. 123 | /// 124 | NumberLinkCollectionNodes: USHORT; 125 | /// 126 | /// Number of input button capabilities structures. 127 | /// 128 | NumberInputButtonCaps: USHORT; 129 | /// 130 | /// Number of input value capabilities structures. 131 | /// 132 | NumberInputValueCaps: USHORT; 133 | /// 134 | /// Number of input data indices. 135 | /// 136 | NumberInputDataIndices: USHORT; 137 | /// 138 | /// Number of output button capabilities structures. 139 | /// 140 | NumberOutputButtonCaps: USHORT; 141 | /// 142 | /// Number of output value capabilities structures. 143 | /// 144 | NumberOutputValueCaps: USHORT; 145 | /// 146 | /// Number of output data indices. 147 | /// 148 | NumberOutputDataIndices: USHORT; 149 | /// 150 | /// Number of feature button capabilities structures. 151 | /// 152 | NumberFeatureButtonCaps: USHORT; 153 | /// 154 | /// Number of feature value capabilities structures. 155 | /// 156 | NumberFeatureValueCaps: USHORT; 157 | /// 158 | /// Number of feature data indices. 159 | /// 160 | NumberFeatureDataIndices: USHORT; 161 | end; 162 | PHIDP_CAPS = ^HIDP_CAPS; 163 | 164 | type 165 | /// 166 | /// Structure used to receive device event notifications. This header is part 167 | /// of a larger structure that varies depending on the type of event being reported. 168 | /// 169 | DEV_BROADCAST_HDR = packed record 170 | /// 171 | /// Size of this structure, in bytes. This size includes the size of the 172 | /// header and any additional data that follows the header. 173 | /// 174 | dbch_size: DWORD; 175 | /// 176 | /// The type of device being described. 177 | /// 178 | dbch_devicetype: DWORD; 179 | /// 180 | /// Reserved; do not use. 181 | /// 182 | dbch_reserved: DWORD; 183 | end; 184 | PDEV_BROADCAST_HDR = ^DEV_BROADCAST_HDR; 185 | 186 | type 187 | /// 188 | /// Structure that defines a device interface class notification filter. 189 | /// It specifies the class of device interfaces that an application wants 190 | /// to receive notifications for. 191 | /// 192 | DEV_BROADCAST_DEVICEINTERFACE = record 193 | /// 194 | /// Size of this structure, in bytes. This size includes this header and 195 | /// the device interface name that follows the header. 196 | /// 197 | dbcc_size: DWORD; 198 | /// 199 | /// Set to DBT_DEVTYP_DEVICEINTERFACE. 200 | /// 201 | dbcc_devicetype: DWORD; 202 | /// 203 | /// Reserved; do not use. 204 | /// 205 | dbcc_reserved: DWORD; 206 | /// 207 | /// The GUID for the interface device class. 208 | /// 209 | dbcc_classguid: TGUID; 210 | /// 211 | /// The first character of the device interface name. This is a variable-length 212 | /// field that specifies the null-terminated name of the device interface. 213 | /// 214 | dbcc_name: short; 215 | end; 216 | PDEV_BROADCAST_DEVICEINTERFACE = ^DEV_BROADCAST_DEVICEINTERFACE; 217 | 218 | implementation 219 | 220 | end. 221 | -------------------------------------------------------------------------------- /Units/untKeyMacro.pas: -------------------------------------------------------------------------------- 1 | unit untKeyMacro; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.Menus, 8 | HID.MacroKeyboard, HID.MacroKeyboard.Config, HID.MacroKeyboard.Hotkey; 9 | 10 | type 11 | TfrmKeyMacro = class(TForm) 12 | pnlBottom: TPanel; 13 | btnOK: TButton; 14 | btnCancel: TButton; 15 | rgMouse: TRadioGroup; 16 | rgMacroType: TRadioGroup; 17 | gbModifiers: TGroupBox; 18 | cbCtrl: TCheckBox; 19 | cbAlt: TCheckBox; 20 | cbShift: TCheckBox; 21 | cbWin: TCheckBox; 22 | rgMedia: TRadioGroup; 23 | gbName: TGroupBox; 24 | edtName: TEdit; 25 | gbKeyboard: TGroupBox; 26 | Panel1: TPanel; 27 | Label1: TLabel; 28 | Panel2: TPanel; 29 | Label2: TLabel; 30 | Panel3: TPanel; 31 | Label3: TLabel; 32 | Panel4: TPanel; 33 | Label4: TLabel; 34 | Panel5: TPanel; 35 | Label5: TLabel; 36 | edtKey1: TMacroKeyboardHotKey; 37 | btnClear1: TButton; 38 | edtKey2: TMacroKeyboardHotKey; 39 | btnClear2: TButton; 40 | edtKey3: TMacroKeyboardHotKey; 41 | btnClear3: TButton; 42 | edtKey4: TMacroKeyboardHotKey; 43 | btnClear4: TButton; 44 | edtKey5: TMacroKeyboardHotKey; 45 | btnClear5: TButton; 46 | procedure rgMacroTypeClick(Sender: TObject); 47 | procedure btnClear1Click(Sender: TObject); 48 | public 49 | /// 50 | /// Execute Key Macro dialog 51 | /// 52 | function Execute(const KeyIndex: Integer; const Key: TMacroKey): TModalResult; 53 | /// 54 | /// Update Macro Key 55 | /// 56 | procedure UpdateMacroKey(const Key: TMacroKey); 57 | end; 58 | 59 | var 60 | frmKeyMacro: TfrmKeyMacro; 61 | 62 | implementation 63 | 64 | {$R *.dfm} 65 | 66 | //------------------------------------------------------------------------------ 67 | // EXECUTE DIALOG 68 | //------------------------------------------------------------------------------ 69 | function TfrmKeyMacro.Execute(const KeyIndex: Integer; const Key: TMacroKey): TModalResult; 70 | 71 | procedure LoadKeySettings; 72 | begin 73 | // Set Name 74 | edtName.Text := Key.Name; 75 | 76 | // Set Modifiers 77 | cbCtrl.Checked := Key.Ctrl; 78 | cbAlt.Checked := Key.Alt; 79 | cbShift.Checked := Key.Shift; 80 | cbWin.Checked := Key.Win; 81 | 82 | // Set macro type 83 | if (Key.Key1 <> '') or (Key.Key2 <> '') or (Key.Key3 <> '') or 84 | (Key.Key4 <> '') or (Key.Key5 <> '') or ((Key.Mouse = '') and 85 | (Key.Media = '')) then rgMacroType.ItemIndex := 0 else 86 | if (Key.Mouse <> '') then rgMacroType.ItemIndex := 1 else 87 | if (Key.Media <> '') then rgMacroType.ItemIndex := 2; 88 | 89 | // Set Mouse 90 | if Key.Mouse = MouseLeft then rgMouse.ItemIndex := 0; 91 | if Key.Mouse = MouseMiddle then rgMouse.ItemIndex := 2; 92 | if Key.Mouse = MouseRight then rgMouse.ItemIndex := 4; 93 | if Key.Mouse = WheelUp then rgMouse.ItemIndex := 3; 94 | if Key.Mouse = WheelDown then rgMouse.ItemIndex := 1; 95 | if Key.Mouse = '' then rgMouse.ItemIndex := 0; 96 | 97 | // Set Media 98 | if Key.Media = MediaPlayPause then rgMedia.ItemIndex := 0; 99 | if Key.Media = MediaPrevious then rgMedia.ItemIndex := 3; 100 | if Key.Media = MediaNext then rgMedia.ItemIndex := 5; 101 | if Key.Media = MediaMute then rgMedia.ItemIndex := 1; 102 | if Key.Media = MediaVolumeUp then rgMedia.ItemIndex := 2; 103 | if Key.Media = MediaVolumeDown then rgMedia.ItemIndex := 4; 104 | if Key.Media = '' then rgMedia.ItemIndex := 0; 105 | 106 | // Set Keys 107 | edtKey1.Text := Key.Key1; 108 | edtKey2.Text := Key.Key2; 109 | edtKey3.Text := Key.Key3; 110 | edtKey4.Text := Key.Key4; 111 | edtKey5.Text := Key.Key5; 112 | end; 113 | 114 | begin 115 | // Set form height 116 | Height := 470; 117 | // Set form width 118 | Width := 370; 119 | // Load key settings 120 | LoadKeySettings; 121 | // Set caption 122 | Caption := Format('Set Macro for Key %d', [KeyIndex + 1]); 123 | // Show dialog modal 124 | Result := ShowModal; 125 | end; 126 | 127 | //------------------------------------------------------------------------------ 128 | // UPDATE MACRO KEY 129 | //------------------------------------------------------------------------------ 130 | procedure TfrmKeyMacro.UpdateMacroKey(const Key: TMacroKey); 131 | begin 132 | // Set Name 133 | Key.Name := edtName.Text; 134 | 135 | // Set Modifiers 136 | Key.Ctrl := cbCtrl.Checked; 137 | Key.Alt := cbAlt.Checked; 138 | Key.Shift := cbShift.Checked; 139 | Key.Win := cbWin.Checked; 140 | 141 | // Set Key type 142 | Key.&Type := rgMacroType.ItemIndex; 143 | 144 | case rgMacroType.ItemIndex of 145 | 0: // Keyboard 146 | begin 147 | // Clear Mouse and Media 148 | Key.Mouse := ''; 149 | Key.Media := ''; 150 | 151 | // Set Keys 152 | Key.Key1 := edtKey1.Text; 153 | Key.Key2 := edtKey2.Text; 154 | Key.Key3 := edtKey3.Text; 155 | Key.Key4 := edtKey4.Text; 156 | Key.Key5 := edtKey5.Text; 157 | end; 158 | 159 | 1: // Mouse 160 | begin 161 | // Clear Media and Keyboard 162 | Key.Media := ''; 163 | Key.Key1 := ''; 164 | Key.Key2 := ''; 165 | Key.Key3 := ''; 166 | Key.Key4 := ''; 167 | Key.Key5 := ''; 168 | 169 | // Set Mouse 170 | case rgMouse.ItemIndex of 171 | 0: Key.Mouse := MouseLeft; 172 | 1: Key.Mouse := WheelDown; 173 | 2: Key.Mouse := MouseMiddle; 174 | 3: Key.Mouse := WheelUp; 175 | 4: Key.Mouse := MouseRight; 176 | end; 177 | end; 178 | 179 | 2: // Media 180 | begin 181 | // Clear Mouse and Keyboard 182 | Key.Mouse := ''; 183 | Key.Key1 := ''; 184 | Key.Key2 := ''; 185 | Key.Key3 := ''; 186 | Key.Key4 := ''; 187 | Key.Key5 := ''; 188 | 189 | // Set Media 190 | case rgMedia.ItemIndex of 191 | 0: Key.Media := MediaPlayPause; 192 | 1: Key.Media := MediaMute; 193 | 2: Key.Media := MediaVolumeUp; 194 | 3: Key.Media := MediaPrevious; 195 | 4: Key.Media := MediaVolumeDown; 196 | 5: Key.Media := MediaNext; 197 | end; 198 | end; 199 | end; 200 | end; 201 | 202 | //------------------------------------------------------------------------------ 203 | // CHANGE MACRO TYPE 204 | //------------------------------------------------------------------------------ 205 | procedure TfrmKeyMacro.rgMacroTypeClick(Sender: TObject); 206 | begin 207 | case rgMacroType.ItemIndex of 208 | 0: begin 209 | gbKeyboard.Visible := True; 210 | rgMouse.Visible := False; 211 | rgMedia.Visible := False; 212 | end; 213 | 214 | 1: begin 215 | rgMouse.ItemIndex := 0; 216 | gbKeyboard.Visible := False; 217 | rgMouse.Visible := True; 218 | rgMedia.Visible := False; 219 | end; 220 | 221 | 2: begin 222 | rgMedia.ItemIndex := 0; 223 | gbKeyboard.Visible := False; 224 | rgMouse.Visible := False; 225 | rgMedia.Visible := True; 226 | end; 227 | end; 228 | end; 229 | 230 | //------------------------------------------------------------------------------ 231 | // CLEAR KEYBOARD KEY 232 | //------------------------------------------------------------------------------ 233 | procedure TfrmKeyMacro.btnClear1Click(Sender: TObject); 234 | begin 235 | case (Sender as TButton).Tag of 236 | 1: edtKey1.Clear; 237 | 2: edtKey2.Clear; 238 | 3: edtKey3.Clear; 239 | 4: edtKey4.Clear; 240 | 5: edtKey5.Clear; 241 | end; 242 | end; 243 | 244 | end. 245 | -------------------------------------------------------------------------------- /Units/untKnobMacro.pas: -------------------------------------------------------------------------------- 1 | unit untKnobMacro; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.Menus, 8 | HID.MacroKeyboard, HID.MacroKeyboard.Config, HID.MacroKeyboard.Hotkey; 9 | 10 | type 11 | TfrmKnobMacro = class(TForm) 12 | pnlBottom: TPanel; 13 | btnOK: TButton; 14 | btnCancel: TButton; 15 | rgMouse: TRadioGroup; 16 | rgMacroType: TRadioGroup; 17 | gbModifiers: TGroupBox; 18 | cbCtrl: TCheckBox; 19 | cbAlt: TCheckBox; 20 | cbShift: TCheckBox; 21 | cbWin: TCheckBox; 22 | rgMedia: TRadioGroup; 23 | gbName: TGroupBox; 24 | edtName: TEdit; 25 | gbKeyboard: TGroupBox; 26 | Panel1: TPanel; 27 | Label1: TLabel; 28 | Panel2: TPanel; 29 | Label2: TLabel; 30 | Panel3: TPanel; 31 | Label3: TLabel; 32 | Panel4: TPanel; 33 | Label4: TLabel; 34 | Panel5: TPanel; 35 | Label5: TLabel; 36 | edtKey1: TMacroKeyboardHotKey; 37 | btnClear1: TButton; 38 | edtKey2: TMacroKeyboardHotKey; 39 | btnClear2: TButton; 40 | edtKey3: TMacroKeyboardHotKey; 41 | btnClear3: TButton; 42 | edtKey4: TMacroKeyboardHotKey; 43 | btnClear4: TButton; 44 | edtKey5: TMacroKeyboardHotKey; 45 | btnClear5: TButton; 46 | procedure rgMacroTypeClick(Sender: TObject); 47 | procedure btnClear1Click(Sender: TObject); 48 | public 49 | /// 50 | /// Execute Knob Macro dialog 51 | /// 52 | function Execute(const KeyIndex: Integer; const Key: TMacroKey; const Action: string): TModalResult; 53 | /// 54 | /// Update Macro Key 55 | /// 56 | procedure UpdateMacroKey(const Key: TMacroKey); 57 | end; 58 | 59 | var 60 | frmKnobMacro: TfrmKnobMacro; 61 | 62 | implementation 63 | 64 | {$R *.dfm} 65 | 66 | //------------------------------------------------------------------------------ 67 | // EXECUTE DIALOG 68 | //------------------------------------------------------------------------------ 69 | function TfrmKnobMacro.Execute(const KeyIndex: Integer; const Key: TMacroKey; const Action: string): TModalResult; 70 | 71 | procedure LoadKeySettings; 72 | begin 73 | // Set Name 74 | edtName.Text := Key.Name; 75 | 76 | // Set Modifiers 77 | cbCtrl.Checked := Key.Ctrl; 78 | cbAlt.Checked := Key.Alt; 79 | cbShift.Checked := Key.Shift; 80 | cbWin.Checked := Key.Win; 81 | 82 | // Set macro type 83 | if (Key.Key1 <> '') or (Key.Key2 <> '') or (Key.Key3 <> '') or 84 | (Key.Key4 <> '') or (Key.Key5 <> '') or ((Key.Mouse = '') and 85 | (Key.Media = '')) then rgMacroType.ItemIndex := 0 else 86 | if (Key.Mouse <> '') then rgMacroType.ItemIndex := 1 else 87 | if (Key.Media <> '') then rgMacroType.ItemIndex := 2; 88 | 89 | // Set Mouse 90 | if Key.Mouse = MouseLeft then rgMouse.ItemIndex := 0; 91 | if Key.Mouse = MouseMiddle then rgMouse.ItemIndex := 2; 92 | if Key.Mouse = MouseRight then rgMouse.ItemIndex := 4; 93 | if Key.Mouse = WheelUp then rgMouse.ItemIndex := 3; 94 | if Key.Mouse = WheelDown then rgMouse.ItemIndex := 1; 95 | if Key.Mouse = '' then rgMouse.ItemIndex := 0; 96 | 97 | // Set Media 98 | if Key.Media = MediaPlayPause then rgMedia.ItemIndex := 0; 99 | if Key.Media = MediaPrevious then rgMedia.ItemIndex := 3; 100 | if Key.Media = MediaNext then rgMedia.ItemIndex := 5; 101 | if Key.Media = MediaMute then rgMedia.ItemIndex := 1; 102 | if Key.Media = MediaVolumeUp then rgMedia.ItemIndex := 2; 103 | if Key.Media = MediaVolumeDown then rgMedia.ItemIndex := 4; 104 | if Key.Media = '' then rgMedia.ItemIndex := 0; 105 | 106 | // Set Keys 107 | edtKey1.Text := Key.Key1; 108 | edtKey2.Text := Key.Key2; 109 | edtKey3.Text := Key.Key3; 110 | edtKey4.Text := Key.Key4; 111 | edtKey5.Text := Key.Key5; 112 | end; 113 | 114 | begin 115 | // Set form height 116 | Height := 470; 117 | // Set form width 118 | Width := 370; 119 | // Load key settings 120 | LoadKeySettings; 121 | // Set caption 122 | Caption := Format('Set Macro for Knob %d - %s', [KeyIndex - 11, Action]); 123 | // Show dialog modal 124 | Result := ShowModal; 125 | end; 126 | 127 | //------------------------------------------------------------------------------ 128 | // UPDATE MACRO KEY 129 | //------------------------------------------------------------------------------ 130 | procedure TfrmKnobMacro.UpdateMacroKey(const Key: TMacroKey); 131 | begin 132 | // Set Name 133 | Key.Name := edtName.Text; 134 | 135 | // Set Modifiers 136 | Key.Ctrl := cbCtrl.Checked; 137 | Key.Alt := cbAlt.Checked; 138 | Key.Shift := cbShift.Checked; 139 | Key.Win := cbWin.Checked; 140 | 141 | // Set Key type 142 | Key.&Type := rgMacroType.ItemIndex; 143 | 144 | case rgMacroType.ItemIndex of 145 | 0: // Keyboard 146 | begin 147 | // Clear Mouse and Media 148 | Key.Mouse := ''; 149 | Key.Media := ''; 150 | 151 | // Set Keys 152 | Key.Key1 := edtKey1.Text; 153 | Key.Key2 := edtKey2.Text; 154 | Key.Key3 := edtKey3.Text; 155 | Key.Key4 := edtKey4.Text; 156 | Key.Key5 := edtKey5.Text; 157 | end; 158 | 159 | 1: // Mouse 160 | begin 161 | // Clear Media and Keyboard 162 | Key.Media := ''; 163 | Key.Key1 := ''; 164 | Key.Key2 := ''; 165 | Key.Key3 := ''; 166 | Key.Key4 := ''; 167 | Key.Key5 := ''; 168 | 169 | // Set Mouse 170 | case rgMouse.ItemIndex of 171 | 0: Key.Mouse := MouseLeft; 172 | 1: Key.Mouse := WheelDown; 173 | 2: Key.Mouse := MouseMiddle; 174 | 3: Key.Mouse := WheelUp; 175 | 4: Key.Mouse := MouseRight; 176 | end; 177 | end; 178 | 179 | 2: // Media 180 | begin 181 | // Clear Mouse and Keyboard 182 | Key.Mouse := ''; 183 | Key.Key1 := ''; 184 | Key.Key2 := ''; 185 | Key.Key3 := ''; 186 | Key.Key4 := ''; 187 | Key.Key5 := ''; 188 | 189 | // Set Media 190 | case rgMedia.ItemIndex of 191 | 0: Key.Media := MediaPlayPause; 192 | 1: Key.Media := MediaMute; 193 | 2: Key.Media := MediaVolumeUp; 194 | 3: Key.Media := MediaPrevious; 195 | 4: Key.Media := MediaVolumeDown; 196 | 5: Key.Media := MediaNext; 197 | end; 198 | end; 199 | end; 200 | end; 201 | 202 | //------------------------------------------------------------------------------ 203 | // CHANGE MACRO TYPE 204 | //------------------------------------------------------------------------------ 205 | procedure TfrmKnobMacro.rgMacroTypeClick(Sender: TObject); 206 | begin 207 | case rgMacroType.ItemIndex of 208 | 0: begin 209 | gbKeyboard.Visible := True; 210 | rgMouse.Visible := False; 211 | rgMedia.Visible := False; 212 | end; 213 | 214 | 1: begin 215 | rgMouse.ItemIndex := 0; 216 | gbKeyboard.Visible := False; 217 | rgMouse.Visible := True; 218 | rgMedia.Visible := False; 219 | end; 220 | 221 | 2: begin 222 | rgMedia.ItemIndex := 0; 223 | gbKeyboard.Visible := False; 224 | rgMouse.Visible := False; 225 | rgMedia.Visible := True; 226 | end; 227 | end; 228 | end; 229 | 230 | //------------------------------------------------------------------------------ 231 | // CLEAR KEYBOARD KEY 232 | //------------------------------------------------------------------------------ 233 | procedure TfrmKnobMacro.btnClear1Click(Sender: TObject); 234 | begin 235 | case (Sender as TButton).Tag of 236 | 1: edtKey1.Clear; 237 | 2: edtKey2.Clear; 238 | 3: edtKey3.Clear; 239 | 4: edtKey4.Clear; 240 | 5: edtKey5.Clear; 241 | end; 242 | end; 243 | 244 | end. 245 | -------------------------------------------------------------------------------- /Units/untKnobMacro.dfm: -------------------------------------------------------------------------------- 1 | object frmKnobMacro: TfrmKnobMacro 2 | Left = 0 3 | Top = 0 4 | BorderStyle = bsDialog 5 | Caption = 'frmKnobMacro' 6 | ClientHeight = 586 7 | ClientWidth = 358 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -12 12 | Font.Name = 'Segoe UI' 13 | Font.Style = [] 14 | Position = poMainFormCenter 15 | TextHeight = 15 16 | object pnlBottom: TPanel 17 | Left = 0 18 | Top = 546 19 | Width = 358 20 | Height = 40 21 | Align = alBottom 22 | BevelOuter = bvNone 23 | ShowCaption = False 24 | TabOrder = 0 25 | ExplicitTop = 545 26 | ExplicitWidth = 354 27 | object btnOK: TButton 28 | AlignWithMargins = True 29 | Left = 156 30 | Top = 7 31 | Width = 93 32 | Height = 26 33 | Margins.Left = 0 34 | Margins.Top = 7 35 | Margins.Right = 8 36 | Margins.Bottom = 7 37 | Align = alRight 38 | Caption = 'OK' 39 | ModalResult = 1 40 | TabOrder = 0 41 | ExplicitLeft = 152 42 | end 43 | object btnCancel: TButton 44 | AlignWithMargins = True 45 | Left = 257 46 | Top = 7 47 | Width = 93 48 | Height = 26 49 | Margins.Left = 0 50 | Margins.Top = 7 51 | Margins.Right = 8 52 | Margins.Bottom = 7 53 | Align = alRight 54 | Caption = 'Cancel' 55 | ModalResult = 2 56 | TabOrder = 1 57 | ExplicitLeft = 253 58 | end 59 | end 60 | object rgMouse: TRadioGroup 61 | AlignWithMargins = True 62 | Left = 8 63 | Top = 389 64 | Width = 342 65 | Height = 68 66 | Margins.Left = 8 67 | Margins.Top = 4 68 | Margins.Right = 8 69 | Margins.Bottom = 0 70 | Align = alTop 71 | Caption = ' Mouse: ' 72 | Columns = 3 73 | Items.Strings = ( 74 | 'Left Button' 75 | 'Wheel Down' 76 | 'Middle Button' 77 | 'Wheel Up' 78 | 'Right Button') 79 | TabOrder = 1 80 | Visible = False 81 | ExplicitWidth = 338 82 | end 83 | object rgMacroType: TRadioGroup 84 | AlignWithMargins = True 85 | Left = 8 86 | Top = 61 87 | Width = 342 88 | Height = 53 89 | Margins.Left = 8 90 | Margins.Top = 4 91 | Margins.Right = 8 92 | Margins.Bottom = 0 93 | Align = alTop 94 | Caption = ' Macro Type: ' 95 | Columns = 3 96 | ItemIndex = 0 97 | Items.Strings = ( 98 | 'Keyboard' 99 | 'Mouse' 100 | 'Media') 101 | TabOrder = 2 102 | OnClick = rgMacroTypeClick 103 | ExplicitWidth = 338 104 | end 105 | object gbModifiers: TGroupBox 106 | AlignWithMargins = True 107 | Left = 8 108 | Top = 118 109 | Width = 342 110 | Height = 76 111 | Margins.Left = 8 112 | Margins.Top = 4 113 | Margins.Right = 8 114 | Margins.Bottom = 0 115 | Align = alTop 116 | Caption = ' Modifier Keys: ' 117 | TabOrder = 3 118 | ExplicitWidth = 338 119 | object cbCtrl: TCheckBox 120 | Left = 8 121 | Top = 20 122 | Width = 97 123 | Height = 17 124 | Caption = 'Ctrl (Cmd)' 125 | TabOrder = 0 126 | end 127 | object cbAlt: TCheckBox 128 | Left = 8 129 | Top = 46 130 | Width = 97 131 | Height = 17 132 | Caption = 'Alt (Option)' 133 | TabOrder = 1 134 | end 135 | object cbShift: TCheckBox 136 | Left = 125 137 | Top = 20 138 | Width = 97 139 | Height = 17 140 | Caption = 'Shift' 141 | TabOrder = 2 142 | end 143 | object cbWin: TCheckBox 144 | Left = 125 145 | Top = 46 146 | Width = 97 147 | Height = 17 148 | Caption = 'Win' 149 | TabOrder = 3 150 | end 151 | end 152 | object rgMedia: TRadioGroup 153 | AlignWithMargins = True 154 | Left = 8 155 | Top = 461 156 | Width = 342 157 | Height = 68 158 | Margins.Left = 8 159 | Margins.Top = 4 160 | Margins.Right = 8 161 | Margins.Bottom = 0 162 | Align = alTop 163 | Caption = ' Media: ' 164 | Columns = 3 165 | Items.Strings = ( 166 | 'Play/Pause' 167 | 'Mute' 168 | 'Volume Up' 169 | 'Previous Track' 170 | 'Volume Down' 171 | 'Next Track') 172 | TabOrder = 4 173 | Visible = False 174 | ExplicitWidth = 338 175 | end 176 | object gbName: TGroupBox 177 | AlignWithMargins = True 178 | Left = 8 179 | Top = 4 180 | Width = 342 181 | Height = 53 182 | Margins.Left = 8 183 | Margins.Top = 4 184 | Margins.Right = 8 185 | Margins.Bottom = 0 186 | Align = alTop 187 | Caption = ' Name: ' 188 | TabOrder = 5 189 | ExplicitWidth = 338 190 | object edtName: TEdit 191 | AlignWithMargins = True 192 | Left = 10 193 | Top = 20 194 | Width = 322 195 | Height = 21 196 | Margins.Left = 8 197 | Margins.Right = 8 198 | Margins.Bottom = 10 199 | Align = alClient 200 | TabOrder = 0 201 | TextHint = 'Enter a name for this macro key..' 202 | ExplicitWidth = 318 203 | ExplicitHeight = 23 204 | end 205 | end 206 | object gbKeyboard: TGroupBox 207 | AlignWithMargins = True 208 | Left = 8 209 | Top = 198 210 | Width = 342 211 | Height = 187 212 | Margins.Left = 8 213 | Margins.Top = 4 214 | Margins.Right = 8 215 | Margins.Bottom = 0 216 | Align = alTop 217 | Caption = ' Keyboard: ' 218 | TabOrder = 6 219 | ExplicitWidth = 338 220 | object Panel1: TPanel 221 | Left = 2 222 | Top = 17 223 | Width = 338 224 | Height = 32 225 | Align = alTop 226 | BevelOuter = bvNone 227 | ShowCaption = False 228 | TabOrder = 0 229 | ExplicitWidth = 334 230 | object Label1: TLabel 231 | Left = 0 232 | Top = 0 233 | Width = 41 234 | Height = 32 235 | Align = alLeft 236 | Alignment = taRightJustify 237 | AutoSize = False 238 | Caption = 'Key 1:' 239 | Layout = tlCenter 240 | end 241 | object edtKey1: TMacroKeyboardHotKey 242 | AlignWithMargins = True 243 | Left = 49 244 | Top = 6 245 | Width = 198 246 | Height = 22 247 | Margins.Left = 8 248 | Margins.Top = 6 249 | Margins.Right = 0 250 | Margins.Bottom = 4 251 | Align = alClient 252 | ReadOnly = True 253 | TabOrder = 0 254 | ExplicitWidth = 194 255 | ExplicitHeight = 23 256 | end 257 | object btnClear1: TButton 258 | Tag = 1 259 | AlignWithMargins = True 260 | Left = 255 261 | Top = 4 262 | Width = 75 263 | Height = 24 264 | Margins.Left = 8 265 | Margins.Top = 4 266 | Margins.Right = 8 267 | Margins.Bottom = 4 268 | Align = alRight 269 | Caption = 'Clear' 270 | TabOrder = 1 271 | OnClick = btnClear1Click 272 | ExplicitLeft = 251 273 | end 274 | end 275 | object Panel2: TPanel 276 | Left = 2 277 | Top = 49 278 | Width = 338 279 | Height = 32 280 | Align = alTop 281 | BevelOuter = bvNone 282 | ShowCaption = False 283 | TabOrder = 1 284 | ExplicitWidth = 334 285 | object Label2: TLabel 286 | Left = 0 287 | Top = 0 288 | Width = 41 289 | Height = 32 290 | Align = alLeft 291 | Alignment = taRightJustify 292 | AutoSize = False 293 | Caption = 'Key 2:' 294 | Layout = tlCenter 295 | end 296 | object edtKey2: TMacroKeyboardHotKey 297 | AlignWithMargins = True 298 | Left = 49 299 | Top = 6 300 | Width = 198 301 | Height = 22 302 | Margins.Left = 8 303 | Margins.Top = 6 304 | Margins.Right = 0 305 | Margins.Bottom = 4 306 | Align = alClient 307 | ReadOnly = True 308 | TabOrder = 0 309 | ExplicitWidth = 194 310 | ExplicitHeight = 23 311 | end 312 | object btnClear2: TButton 313 | Tag = 2 314 | AlignWithMargins = True 315 | Left = 255 316 | Top = 4 317 | Width = 75 318 | Height = 24 319 | Margins.Left = 8 320 | Margins.Top = 4 321 | Margins.Right = 8 322 | Margins.Bottom = 4 323 | Align = alRight 324 | Caption = 'Clear' 325 | TabOrder = 1 326 | OnClick = btnClear1Click 327 | ExplicitLeft = 251 328 | end 329 | end 330 | object Panel3: TPanel 331 | Left = 2 332 | Top = 81 333 | Width = 338 334 | Height = 32 335 | Align = alTop 336 | BevelOuter = bvNone 337 | ShowCaption = False 338 | TabOrder = 2 339 | ExplicitWidth = 334 340 | object Label3: TLabel 341 | Left = 0 342 | Top = 0 343 | Width = 41 344 | Height = 32 345 | Align = alLeft 346 | Alignment = taRightJustify 347 | AutoSize = False 348 | Caption = 'Key 3:' 349 | Layout = tlCenter 350 | end 351 | object edtKey3: TMacroKeyboardHotKey 352 | AlignWithMargins = True 353 | Left = 49 354 | Top = 6 355 | Width = 198 356 | Height = 22 357 | Margins.Left = 8 358 | Margins.Top = 6 359 | Margins.Right = 0 360 | Margins.Bottom = 4 361 | Align = alClient 362 | ReadOnly = True 363 | TabOrder = 0 364 | ExplicitWidth = 194 365 | ExplicitHeight = 23 366 | end 367 | object btnClear3: TButton 368 | Tag = 3 369 | AlignWithMargins = True 370 | Left = 255 371 | Top = 4 372 | Width = 75 373 | Height = 24 374 | Margins.Left = 8 375 | Margins.Top = 4 376 | Margins.Right = 8 377 | Margins.Bottom = 4 378 | Align = alRight 379 | Caption = 'Clear' 380 | TabOrder = 1 381 | OnClick = btnClear1Click 382 | ExplicitLeft = 251 383 | end 384 | end 385 | object Panel4: TPanel 386 | Left = 2 387 | Top = 113 388 | Width = 338 389 | Height = 32 390 | Align = alTop 391 | BevelOuter = bvNone 392 | ShowCaption = False 393 | TabOrder = 3 394 | ExplicitWidth = 334 395 | object Label4: TLabel 396 | Left = 0 397 | Top = 0 398 | Width = 41 399 | Height = 32 400 | Align = alLeft 401 | Alignment = taRightJustify 402 | AutoSize = False 403 | Caption = 'Key 4:' 404 | Layout = tlCenter 405 | end 406 | object edtKey4: TMacroKeyboardHotKey 407 | AlignWithMargins = True 408 | Left = 49 409 | Top = 6 410 | Width = 198 411 | Height = 22 412 | Margins.Left = 8 413 | Margins.Top = 6 414 | Margins.Right = 0 415 | Margins.Bottom = 4 416 | Align = alClient 417 | ReadOnly = True 418 | TabOrder = 0 419 | ExplicitWidth = 194 420 | ExplicitHeight = 23 421 | end 422 | object btnClear4: TButton 423 | Tag = 4 424 | AlignWithMargins = True 425 | Left = 255 426 | Top = 4 427 | Width = 75 428 | Height = 24 429 | Margins.Left = 8 430 | Margins.Top = 4 431 | Margins.Right = 8 432 | Margins.Bottom = 4 433 | Align = alRight 434 | Caption = 'Clear' 435 | TabOrder = 1 436 | OnClick = btnClear1Click 437 | ExplicitLeft = 251 438 | end 439 | end 440 | object Panel5: TPanel 441 | Left = 2 442 | Top = 145 443 | Width = 338 444 | Height = 32 445 | Align = alTop 446 | BevelOuter = bvNone 447 | ShowCaption = False 448 | TabOrder = 4 449 | ExplicitWidth = 334 450 | object Label5: TLabel 451 | Left = 0 452 | Top = 0 453 | Width = 41 454 | Height = 32 455 | Align = alLeft 456 | Alignment = taRightJustify 457 | AutoSize = False 458 | Caption = 'Key 5:' 459 | Layout = tlCenter 460 | end 461 | object edtKey5: TMacroKeyboardHotKey 462 | AlignWithMargins = True 463 | Left = 49 464 | Top = 6 465 | Width = 198 466 | Height = 22 467 | Margins.Left = 8 468 | Margins.Top = 6 469 | Margins.Right = 0 470 | Margins.Bottom = 4 471 | Align = alClient 472 | ReadOnly = True 473 | TabOrder = 0 474 | ExplicitWidth = 194 475 | ExplicitHeight = 23 476 | end 477 | object btnClear5: TButton 478 | Tag = 5 479 | AlignWithMargins = True 480 | Left = 255 481 | Top = 4 482 | Width = 75 483 | Height = 24 484 | Margins.Left = 8 485 | Margins.Top = 4 486 | Margins.Right = 8 487 | Margins.Bottom = 4 488 | Align = alRight 489 | Caption = 'Clear' 490 | TabOrder = 1 491 | OnClick = btnClear1Click 492 | ExplicitLeft = 251 493 | end 494 | end 495 | end 496 | end 497 | -------------------------------------------------------------------------------- /Units/untKeyMacro.dfm: -------------------------------------------------------------------------------- 1 | object frmKeyMacro: TfrmKeyMacro 2 | Left = 0 3 | Top = 0 4 | BorderStyle = bsDialog 5 | Caption = 'frmKeyMacro' 6 | ClientHeight = 586 7 | ClientWidth = 358 8 | Color = clBtnFace 9 | Font.Charset = DEFAULT_CHARSET 10 | Font.Color = clWindowText 11 | Font.Height = -12 12 | Font.Name = 'Segoe UI' 13 | Font.Style = [] 14 | Position = poMainFormCenter 15 | TextHeight = 15 16 | object pnlBottom: TPanel 17 | Left = 0 18 | Top = 546 19 | Width = 358 20 | Height = 40 21 | Align = alBottom 22 | BevelOuter = bvNone 23 | ShowCaption = False 24 | TabOrder = 6 25 | ExplicitTop = 545 26 | ExplicitWidth = 354 27 | object btnOK: TButton 28 | AlignWithMargins = True 29 | Left = 156 30 | Top = 7 31 | Width = 93 32 | Height = 26 33 | Margins.Left = 0 34 | Margins.Top = 7 35 | Margins.Right = 8 36 | Margins.Bottom = 7 37 | Align = alRight 38 | Caption = 'OK' 39 | ModalResult = 1 40 | TabOrder = 0 41 | ExplicitLeft = 152 42 | end 43 | object btnCancel: TButton 44 | AlignWithMargins = True 45 | Left = 257 46 | Top = 7 47 | Width = 93 48 | Height = 26 49 | Margins.Left = 0 50 | Margins.Top = 7 51 | Margins.Right = 8 52 | Margins.Bottom = 7 53 | Align = alRight 54 | Caption = 'Cancel' 55 | ModalResult = 2 56 | TabOrder = 1 57 | ExplicitLeft = 253 58 | end 59 | end 60 | object rgMouse: TRadioGroup 61 | AlignWithMargins = True 62 | Left = 8 63 | Top = 389 64 | Width = 346 65 | Height = 68 66 | Margins.Left = 8 67 | Margins.Top = 4 68 | Margins.Right = 4 69 | Margins.Bottom = 0 70 | Align = alTop 71 | Caption = ' Mouse: ' 72 | Columns = 3 73 | Items.Strings = ( 74 | 'Left Button' 75 | 'Wheel Down' 76 | 'Middle Button' 77 | 'Wheel Up' 78 | 'Right Button') 79 | TabOrder = 4 80 | Visible = False 81 | ExplicitWidth = 342 82 | end 83 | object rgMacroType: TRadioGroup 84 | AlignWithMargins = True 85 | Left = 8 86 | Top = 61 87 | Width = 346 88 | Height = 53 89 | Margins.Left = 8 90 | Margins.Top = 4 91 | Margins.Right = 4 92 | Margins.Bottom = 0 93 | Align = alTop 94 | Caption = ' Macro Type: ' 95 | Columns = 3 96 | ItemIndex = 0 97 | Items.Strings = ( 98 | 'Keyboard' 99 | 'Mouse' 100 | 'Media') 101 | TabOrder = 1 102 | OnClick = rgMacroTypeClick 103 | ExplicitWidth = 342 104 | end 105 | object gbModifiers: TGroupBox 106 | AlignWithMargins = True 107 | Left = 8 108 | Top = 118 109 | Width = 346 110 | Height = 76 111 | Margins.Left = 8 112 | Margins.Top = 4 113 | Margins.Right = 4 114 | Margins.Bottom = 0 115 | Align = alTop 116 | Caption = ' Modifier Keys: ' 117 | TabOrder = 2 118 | ExplicitWidth = 342 119 | object cbCtrl: TCheckBox 120 | Left = 8 121 | Top = 20 122 | Width = 97 123 | Height = 17 124 | Caption = 'Ctrl (Cmd)' 125 | TabOrder = 0 126 | end 127 | object cbAlt: TCheckBox 128 | Left = 8 129 | Top = 46 130 | Width = 97 131 | Height = 17 132 | Caption = 'Alt (Option)' 133 | TabOrder = 1 134 | end 135 | object cbShift: TCheckBox 136 | Left = 125 137 | Top = 20 138 | Width = 97 139 | Height = 17 140 | Caption = 'Shift' 141 | TabOrder = 2 142 | end 143 | object cbWin: TCheckBox 144 | Left = 125 145 | Top = 46 146 | Width = 97 147 | Height = 17 148 | Caption = 'Win' 149 | TabOrder = 3 150 | end 151 | end 152 | object rgMedia: TRadioGroup 153 | AlignWithMargins = True 154 | Left = 8 155 | Top = 461 156 | Width = 346 157 | Height = 68 158 | Margins.Left = 8 159 | Margins.Top = 4 160 | Margins.Right = 4 161 | Margins.Bottom = 0 162 | Align = alTop 163 | Caption = ' Media: ' 164 | Columns = 3 165 | Items.Strings = ( 166 | 'Play/Pause' 167 | 'Mute' 168 | 'Volume Up' 169 | 'Previous Track' 170 | 'Volume Down' 171 | 'Next Track') 172 | TabOrder = 5 173 | Visible = False 174 | ExplicitWidth = 342 175 | end 176 | object gbName: TGroupBox 177 | AlignWithMargins = True 178 | Left = 8 179 | Top = 4 180 | Width = 346 181 | Height = 53 182 | Margins.Left = 8 183 | Margins.Top = 4 184 | Margins.Right = 4 185 | Margins.Bottom = 0 186 | Align = alTop 187 | Caption = ' Name: ' 188 | TabOrder = 0 189 | ExplicitWidth = 342 190 | object edtName: TEdit 191 | AlignWithMargins = True 192 | Left = 10 193 | Top = 20 194 | Width = 326 195 | Height = 21 196 | Margins.Left = 8 197 | Margins.Right = 8 198 | Margins.Bottom = 10 199 | Align = alClient 200 | TabOrder = 0 201 | TextHint = 'Enter a name for this macro key..' 202 | ExplicitWidth = 322 203 | ExplicitHeight = 23 204 | end 205 | end 206 | object gbKeyboard: TGroupBox 207 | AlignWithMargins = True 208 | Left = 8 209 | Top = 198 210 | Width = 346 211 | Height = 187 212 | Margins.Left = 8 213 | Margins.Top = 4 214 | Margins.Right = 4 215 | Margins.Bottom = 0 216 | Align = alTop 217 | Caption = ' Keyboard: ' 218 | TabOrder = 3 219 | ExplicitWidth = 342 220 | object Panel1: TPanel 221 | Left = 2 222 | Top = 17 223 | Width = 342 224 | Height = 32 225 | Align = alTop 226 | BevelOuter = bvNone 227 | ShowCaption = False 228 | TabOrder = 0 229 | ExplicitWidth = 338 230 | object Label1: TLabel 231 | Left = 0 232 | Top = 0 233 | Width = 41 234 | Height = 32 235 | Align = alLeft 236 | Alignment = taRightJustify 237 | AutoSize = False 238 | Caption = 'Key 1:' 239 | Layout = tlCenter 240 | end 241 | object edtKey1: TMacroKeyboardHotKey 242 | AlignWithMargins = True 243 | Left = 49 244 | Top = 6 245 | Width = 202 246 | Height = 22 247 | Margins.Left = 8 248 | Margins.Top = 6 249 | Margins.Right = 0 250 | Margins.Bottom = 4 251 | Align = alClient 252 | ReadOnly = True 253 | TabOrder = 0 254 | ExplicitWidth = 198 255 | ExplicitHeight = 23 256 | end 257 | object btnClear1: TButton 258 | Tag = 1 259 | AlignWithMargins = True 260 | Left = 259 261 | Top = 4 262 | Width = 75 263 | Height = 24 264 | Margins.Left = 8 265 | Margins.Top = 4 266 | Margins.Right = 8 267 | Margins.Bottom = 4 268 | Align = alRight 269 | Caption = 'Clear' 270 | TabOrder = 1 271 | OnClick = btnClear1Click 272 | ExplicitLeft = 255 273 | end 274 | end 275 | object Panel2: TPanel 276 | Left = 2 277 | Top = 49 278 | Width = 342 279 | Height = 32 280 | Align = alTop 281 | BevelOuter = bvNone 282 | ShowCaption = False 283 | TabOrder = 1 284 | ExplicitWidth = 338 285 | object Label2: TLabel 286 | Left = 0 287 | Top = 0 288 | Width = 41 289 | Height = 32 290 | Align = alLeft 291 | Alignment = taRightJustify 292 | AutoSize = False 293 | Caption = 'Key 2:' 294 | Layout = tlCenter 295 | end 296 | object edtKey2: TMacroKeyboardHotKey 297 | AlignWithMargins = True 298 | Left = 49 299 | Top = 6 300 | Width = 202 301 | Height = 22 302 | Margins.Left = 8 303 | Margins.Top = 6 304 | Margins.Right = 0 305 | Margins.Bottom = 4 306 | Align = alClient 307 | ReadOnly = True 308 | TabOrder = 0 309 | ExplicitWidth = 198 310 | ExplicitHeight = 23 311 | end 312 | object btnClear2: TButton 313 | Tag = 2 314 | AlignWithMargins = True 315 | Left = 259 316 | Top = 4 317 | Width = 75 318 | Height = 24 319 | Margins.Left = 8 320 | Margins.Top = 4 321 | Margins.Right = 8 322 | Margins.Bottom = 4 323 | Align = alRight 324 | Caption = 'Clear' 325 | TabOrder = 1 326 | OnClick = btnClear1Click 327 | ExplicitLeft = 255 328 | end 329 | end 330 | object Panel3: TPanel 331 | Left = 2 332 | Top = 81 333 | Width = 342 334 | Height = 32 335 | Align = alTop 336 | BevelOuter = bvNone 337 | ShowCaption = False 338 | TabOrder = 2 339 | ExplicitWidth = 338 340 | object Label3: TLabel 341 | Left = 0 342 | Top = 0 343 | Width = 41 344 | Height = 32 345 | Align = alLeft 346 | Alignment = taRightJustify 347 | AutoSize = False 348 | Caption = 'Key 3:' 349 | Layout = tlCenter 350 | end 351 | object edtKey3: TMacroKeyboardHotKey 352 | AlignWithMargins = True 353 | Left = 49 354 | Top = 6 355 | Width = 202 356 | Height = 22 357 | Margins.Left = 8 358 | Margins.Top = 6 359 | Margins.Right = 0 360 | Margins.Bottom = 4 361 | Align = alClient 362 | ReadOnly = True 363 | TabOrder = 0 364 | ExplicitWidth = 198 365 | ExplicitHeight = 23 366 | end 367 | object btnClear3: TButton 368 | Tag = 3 369 | AlignWithMargins = True 370 | Left = 259 371 | Top = 4 372 | Width = 75 373 | Height = 24 374 | Margins.Left = 8 375 | Margins.Top = 4 376 | Margins.Right = 8 377 | Margins.Bottom = 4 378 | Align = alRight 379 | Caption = 'Clear' 380 | TabOrder = 1 381 | OnClick = btnClear1Click 382 | ExplicitLeft = 255 383 | end 384 | end 385 | object Panel4: TPanel 386 | Left = 2 387 | Top = 113 388 | Width = 342 389 | Height = 32 390 | Align = alTop 391 | BevelOuter = bvNone 392 | ShowCaption = False 393 | TabOrder = 3 394 | ExplicitWidth = 338 395 | object Label4: TLabel 396 | Left = 0 397 | Top = 0 398 | Width = 41 399 | Height = 32 400 | Align = alLeft 401 | Alignment = taRightJustify 402 | AutoSize = False 403 | Caption = 'Key 4:' 404 | Layout = tlCenter 405 | end 406 | object edtKey4: TMacroKeyboardHotKey 407 | AlignWithMargins = True 408 | Left = 49 409 | Top = 6 410 | Width = 202 411 | Height = 22 412 | Margins.Left = 8 413 | Margins.Top = 6 414 | Margins.Right = 0 415 | Margins.Bottom = 4 416 | Align = alClient 417 | ReadOnly = True 418 | TabOrder = 0 419 | ExplicitWidth = 198 420 | ExplicitHeight = 23 421 | end 422 | object btnClear4: TButton 423 | Tag = 4 424 | AlignWithMargins = True 425 | Left = 259 426 | Top = 4 427 | Width = 75 428 | Height = 24 429 | Margins.Left = 8 430 | Margins.Top = 4 431 | Margins.Right = 8 432 | Margins.Bottom = 4 433 | Align = alRight 434 | Caption = 'Clear' 435 | TabOrder = 1 436 | OnClick = btnClear1Click 437 | ExplicitLeft = 255 438 | end 439 | end 440 | object Panel5: TPanel 441 | Left = 2 442 | Top = 145 443 | Width = 342 444 | Height = 32 445 | Align = alTop 446 | BevelOuter = bvNone 447 | ShowCaption = False 448 | TabOrder = 4 449 | ExplicitWidth = 338 450 | object Label5: TLabel 451 | Left = 0 452 | Top = 0 453 | Width = 41 454 | Height = 32 455 | Align = alLeft 456 | Alignment = taRightJustify 457 | AutoSize = False 458 | Caption = 'Key 5:' 459 | Layout = tlCenter 460 | end 461 | object edtKey5: TMacroKeyboardHotKey 462 | Tag = 5 463 | AlignWithMargins = True 464 | Left = 49 465 | Top = 6 466 | Width = 202 467 | Height = 22 468 | Margins.Left = 8 469 | Margins.Top = 6 470 | Margins.Right = 0 471 | Margins.Bottom = 4 472 | Align = alClient 473 | ReadOnly = True 474 | TabOrder = 0 475 | ExplicitWidth = 198 476 | ExplicitHeight = 23 477 | end 478 | object btnClear5: TButton 479 | Tag = 5 480 | AlignWithMargins = True 481 | Left = 259 482 | Top = 4 483 | Width = 75 484 | Height = 24 485 | Margins.Left = 8 486 | Margins.Top = 4 487 | Margins.Right = 8 488 | Margins.Bottom = 4 489 | Align = alRight 490 | Caption = 'Clear' 491 | TabOrder = 1 492 | OnClick = btnClear1Click 493 | ExplicitLeft = 255 494 | end 495 | end 496 | end 497 | end 498 | -------------------------------------------------------------------------------- /Units/HID.MacroKeyboard.pas: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // UNIT : HID.MacroKeyboard.pas 3 | // CONTENTS : HID MacroKeyboard Functions and Types 4 | // VERSION : 1.0 5 | // TARGET : Embarcadero Delphi 11 or higher 6 | // AUTHOR : Ernst Reidinga (ERDesigns) 7 | // STATUS : Open source under Apache 2.0 library 8 | // COMPATIBILITY : Windows 7, 8/8.1, 10, 11 9 | // RELEASE DATE : 19/05/2024 10 | //------------------------------------------------------------------------------ 11 | unit HID.MacroKeyboard; 12 | 13 | interface 14 | 15 | uses HID, HID.Constants; 16 | 17 | type 18 | /// 19 | /// Macro Type 20 | /// 21 | TMacroType = Byte; 22 | /// 23 | /// Key Code Type 24 | /// 25 | TKeyCode = Byte; 26 | /// 27 | /// Media Code Type 28 | /// 29 | TMediaCode = Byte; 30 | /// 31 | /// Mouse Key Code Type 32 | /// 33 | TMouseKeyCode = Byte; 34 | /// 35 | /// Mouse Wheel Code Type 36 | /// 37 | TMouseWheelCode = Byte; 38 | /// 39 | /// Modifier Code Type 40 | /// 41 | TModifierCode = Byte; 42 | 43 | type 44 | /// 45 | /// Key Codes Array Type 46 | /// 47 | TKeyCodes = array of TKeyCode; 48 | /// 49 | /// Modifier Codes Array Type 50 | /// 51 | TModifierCodes = array of TModifierCode; 52 | 53 | const 54 | /// 55 | /// Macro Type KEY 56 | /// 57 | MACRO_KEY: TMacroType = $00; 58 | /// 59 | /// Macro Type MOUSE 60 | /// 61 | MACRO_MOUSE: TMacroType = $01; 62 | /// 63 | /// Macro Type MEDIA 64 | /// 65 | MACRO_MEDIA: TMacroType = $02; 66 | 67 | const 68 | /// 69 | /// Key 1 70 | /// 71 | KEYBOARD_KEY1: Byte = $01; 72 | /// 73 | /// Key 2 74 | /// 75 | KEYBOARD_KEY2: Byte = $02; 76 | /// 77 | /// Key 3 78 | /// 79 | KEYBOARD_KEY3: Byte = $03; 80 | /// 81 | /// Key 4 82 | /// 83 | KEYBOARD_KEY4: Byte = $04; 84 | /// 85 | /// Key 5 86 | /// 87 | KEYBOARD_KEY5: Byte = $05; 88 | /// 89 | /// Key 6 90 | /// 91 | KEYBOARD_KEY6: Byte = $06; 92 | /// 93 | /// Key 7 94 | /// 95 | KEYBOARD_KEY7: Byte = $07; 96 | /// 97 | /// Key 8 98 | /// 99 | KEYBOARD_KEY8: Byte = $08; 100 | /// 101 | /// Key 9 102 | /// 103 | KEYBOARD_KEY9: Byte = $09; 104 | /// 105 | /// Key 10 106 | /// 107 | KEYBOARD_KEY10: Byte = $0A; 108 | /// 109 | /// Key 11 110 | /// 111 | KEYBOARD_KEY11: Byte = $0B; 112 | /// 113 | /// Key 12 114 | /// 115 | KEYBOARD_KEY12: Byte = $0C; 116 | 117 | const 118 | /// 119 | /// Rotary encoder 1 - Left 120 | /// 121 | KEYBOARD_ROT1_LEFT: Byte = $A3; 122 | /// 123 | /// Rotary encoder 1 - Click 124 | /// 125 | KEYBOARD_ROT1_CLICK: Byte = $A2; 126 | /// 127 | /// Rotary encoder 1 - Right 128 | /// 129 | KEYBOARD_ROT1_RIGHT: Byte = $A1; 130 | /// 131 | /// Rotary encoder 2 - Left 132 | /// 133 | KEYBOARD_ROT2_LEFT: Byte = $A6; 134 | /// 135 | /// Rotary encoder 2 - Click 136 | /// 137 | KEYBOARD_ROT2_CLICK: Byte = $A5; 138 | /// 139 | /// Rotary encoder 2 - Right 140 | /// 141 | KEYBOARD_ROT2_RIGHT: Byte = $A4; 142 | /// 143 | /// Rotary encoder 3 - Left 144 | /// 145 | KEYBOARD_ROT3_LEFT: Byte = $A9; 146 | /// 147 | /// Rotary encoder 3 - Click 148 | /// 149 | KEYBOARD_ROT3_CLICK: Byte = $A8; 150 | /// 151 | /// Rotary encoder 3 - Right 152 | /// 153 | KEYBOARD_ROT3_RIGHT: Byte = $A7; 154 | 155 | const 156 | /// 157 | /// No key pressed 158 | /// 159 | KEY_NOKEY: TKeyCode = $00; 160 | /// 161 | /// A key 162 | /// 163 | KEY_A: TKeyCode = $04; 164 | /// 165 | /// B key 166 | /// 167 | KEY_B: TKeyCode = $05; 168 | /// 169 | /// C key 170 | /// 171 | KEY_C: TKeyCode = $06; 172 | /// 173 | /// D key 174 | /// 175 | KEY_D: TKeyCode = $07; 176 | /// 177 | /// E key 178 | /// 179 | KEY_E: TKeyCode = $08; 180 | /// 181 | /// F key 182 | /// 183 | KEY_F: TKeyCode = $09; 184 | /// 185 | /// G key 186 | /// 187 | KEY_G: TKeyCode = $0A; 188 | /// 189 | /// H key 190 | /// 191 | KEY_H: TKeyCode = $0B; 192 | /// 193 | /// I key 194 | /// 195 | KEY_I: TKeyCode = $0C; 196 | /// 197 | /// J key 198 | /// 199 | KEY_J: TKeyCode = $0D; 200 | /// 201 | /// K key 202 | /// 203 | KEY_K: TKeyCode = $0E; 204 | /// 205 | /// L key 206 | /// 207 | KEY_L: TKeyCode = $0F; 208 | /// 209 | /// M key 210 | /// 211 | KEY_M: TKeyCode = $10; 212 | /// 213 | /// N key 214 | /// 215 | KEY_N: TKeyCode = $11; 216 | /// 217 | /// O key 218 | /// 219 | KEY_O: TKeyCode = $12; 220 | /// 221 | /// P key 222 | /// 223 | KEY_P: TKeyCode = $13; 224 | /// 225 | /// Q key 226 | /// 227 | KEY_Q: TKeyCode = $14; 228 | /// 229 | /// R key 230 | /// 231 | KEY_R: TKeyCode = $15; 232 | /// 233 | /// S key 234 | /// 235 | KEY_S: TKeyCode = $16; 236 | /// 237 | /// T key 238 | /// 239 | KEY_T: TKeyCode = $17; 240 | /// 241 | /// U key 242 | /// 243 | KEY_U: TKeyCode = $18; 244 | /// 245 | /// V key 246 | /// 247 | KEY_V: TKeyCode = $19; 248 | /// 249 | /// W key 250 | /// 251 | KEY_W: TKeyCode = $1A; 252 | /// 253 | /// X key 254 | /// 255 | KEY_X: TKeyCode = $1B; 256 | /// 257 | /// Y key 258 | /// 259 | KEY_Y: TKeyCode = $1C; 260 | /// 261 | /// Z key 262 | /// 263 | KEY_Z: TKeyCode = $1D; 264 | /// 265 | /// Number 1 key 266 | /// 267 | KEY_N1: TKeyCode = $1E; 268 | /// 269 | /// Number 2 key 270 | /// 271 | KEY_N2: TKeyCode = $1F; 272 | /// 273 | /// Number 3 key 274 | /// 275 | KEY_N3: TKeyCode = $20; 276 | /// 277 | /// Number 4 key 278 | /// 279 | KEY_N4: TKeyCode = $21; 280 | /// 281 | /// Number 5 key 282 | /// 283 | KEY_N5: TKeyCode = $22; 284 | /// 285 | /// Number 6 key 286 | /// 287 | KEY_N6: TKeyCode = $23; 288 | /// 289 | /// Number 7 key 290 | /// 291 | KEY_N7: TKeyCode = $24; 292 | /// 293 | /// Number 8 key 294 | /// 295 | KEY_N8: TKeyCode = $25; 296 | /// 297 | /// Number 9 key 298 | /// 299 | KEY_N9: TKeyCode = $26; 300 | /// 301 | /// Number 0 key 302 | /// 303 | KEY_N0: TKeyCode = $27; 304 | /// 305 | /// Enter key 306 | /// 307 | KEY_ENTER: TKeyCode = $28; 308 | /// 309 | /// Escape key 310 | /// 311 | KEY_ESCAPE: TKeyCode = $29; 312 | /// 313 | /// Backspace key 314 | /// 315 | KEY_BSPACE: TKeyCode = $2A; 316 | /// 317 | /// Tab key 318 | /// 319 | KEY_TAB: TKeyCode = $2B; 320 | /// 321 | /// Space key 322 | /// 323 | KEY_SPACE: TKeyCode = $2C; 324 | /// 325 | /// Minus key 326 | /// 327 | KEY_MINUS: TKeyCode = $2D; 328 | /// 329 | /// Equal key 330 | /// 331 | KEY_EQUAL: TKeyCode = $2E; 332 | /// 333 | /// Left Bracket key 334 | /// 335 | KEY_LBRACKET: TKeyCode = $2F; 336 | /// 337 | /// Right Bracket key 338 | /// 339 | KEY_RBRACKET: TKeyCode = $30; 340 | /// 341 | /// Backslash key 342 | /// 343 | KEY_BSLASH: TKeyCode = $31; 344 | /// 345 | /// Non US Hash key 346 | /// 347 | KEY_NONUS_HASH: TKeyCode = $32; 348 | /// 349 | /// Semicolon key 350 | /// 351 | KEY_SCOLON: TKeyCode = $33; 352 | /// 353 | /// Quote key 354 | /// 355 | KEY_QUOTE: TKeyCode = $34; 356 | /// 357 | /// Grave key 358 | /// 359 | KEY_GRAVE: TKeyCode = $35; 360 | /// 361 | /// Comma key 362 | /// 363 | KEY_COMMA: TKeyCode = $36; 364 | /// 365 | /// Dot key 366 | /// 367 | KEY_DOT: TKeyCode = $37; 368 | /// 369 | /// Slash key 370 | /// 371 | KEY_SLASH: TKeyCode = $38; 372 | /// 373 | /// Capslock key 374 | /// 375 | KEY_CAPSLOCK: TKeyCode = $39; 376 | /// 377 | /// F1 key 378 | /// 379 | KEY_F1: TKeyCode = $3A; 380 | /// 381 | /// F2 key 382 | /// 383 | KEY_F2: TKeyCode = $3B; 384 | /// 385 | /// F3 key 386 | /// 387 | KEY_F3: TKeyCode = $3C; 388 | /// 389 | /// F4 key 390 | /// 391 | KEY_F4: TKeyCode = $3D; 392 | /// 393 | /// F5 key 394 | /// 395 | KEY_F5: TKeyCode = $3E; 396 | /// 397 | /// F6 key 398 | /// 399 | KEY_F6: TKeyCode = $3F; 400 | /// 401 | /// F7 key 402 | /// 403 | KEY_F7: TKeyCode = $40; 404 | /// 405 | /// F8 key 406 | /// 407 | KEY_F8: TKeyCode = $41; 408 | /// 409 | /// F9 key 410 | /// 411 | KEY_F9: TKeyCode = $42; 412 | /// 413 | /// F10 key 414 | /// 415 | KEY_F10: TKeyCode = $43; 416 | /// 417 | /// F11 key 418 | /// 419 | KEY_F11: TKeyCode = $44; 420 | /// 421 | /// F12 key 422 | /// 423 | KEY_F12: TKeyCode = $45; 424 | /// 425 | /// Print Screen key 426 | /// 427 | KEY_PSCREEN: TKeyCode = $46; 428 | /// 429 | /// Scroll Lock key 430 | /// 431 | KEY_SCROLLLOCK: TKeyCode = $47; 432 | /// 433 | /// Pause key 434 | /// 435 | KEY_PAUSE: TKeyCode = $48; 436 | /// 437 | /// Insert key 438 | /// 439 | KEY_INSERT: TKeyCode = $49; 440 | /// 441 | /// Home key 442 | /// 443 | KEY_HOME: TKeyCode = $4A; 444 | /// 445 | /// Page Up key 446 | /// 447 | KEY_PGUP: TKeyCode = $4B; 448 | /// 449 | /// Delete key 450 | /// 451 | KEY_DELETE: TKeyCode = $4C; 452 | /// 453 | /// End key 454 | /// 455 | KEY_END: TKeyCode = $4D; 456 | /// 457 | /// Page Down key 458 | /// 459 | KEY_PGDOWN: TKeyCode = $4E; 460 | /// 461 | /// Right Arrow key 462 | /// 463 | KEY_RIGHT: TKeyCode = $4F; 464 | /// 465 | /// Left Arrow key 466 | /// 467 | KEY_LEFT: TKeyCode = $50; 468 | /// 469 | /// Down Arrow key 470 | /// 471 | KEY_DOWN: TKeyCode = $51; 472 | /// 473 | /// Up Arrow key 474 | /// 475 | KEY_UP: TKeyCode = $52; 476 | /// 477 | /// Num Lock key 478 | /// 479 | KEY_NUMLOCK: TKeyCode = $53; 480 | /// 481 | /// Keypad Slash key 482 | /// 483 | KEY_KP_SLASH: TKeyCode = $54; 484 | /// 485 | /// Keypad Asterisk key 486 | /// 487 | KEY_KP_ASTERISK: TKeyCode = $55; 488 | /// 489 | /// Keypad Minus key 490 | /// 491 | KEY_KP_MINUS: TKeyCode = $56; 492 | /// 493 | /// Keypad Plus key 494 | /// 495 | KEY_KP_PLUS: TKeyCode = $57; 496 | /// 497 | /// Keypad Enter key 498 | /// 499 | KEY_KP_ENTER: TKeyCode = $58; 500 | /// 501 | /// Keypad 1 key 502 | /// 503 | KEY_KP_1: TKeyCode = $59; 504 | /// 505 | /// Keypad 2 key 506 | /// 507 | KEY_KP_2: TKeyCode = $5A; 508 | /// 509 | /// Keypad 3 key 510 | /// 511 | KEY_KP_3: TKeyCode = $5B; 512 | /// 513 | /// Keypad 4 key 514 | /// 515 | KEY_KP_4: TKeyCode = $5C; 516 | /// 517 | /// Keypad 5 key 518 | /// 519 | KEY_KP_5: TKeyCode = $5D; 520 | /// 521 | /// Keypad 6 key 522 | /// 523 | KEY_KP_6: TKeyCode = $5E; 524 | /// 525 | /// Keypad 7 key 526 | /// 527 | KEY_KP_7: TKeyCode = $5F; 528 | /// 529 | /// Keypad 8 key 530 | /// 531 | KEY_KP_8: TKeyCode = $60; 532 | /// 533 | /// Keypad 9 key 534 | /// 535 | KEY_KP_9: TKeyCode = $61; 536 | /// 537 | /// Keypad 0 key 538 | /// 539 | KEY_KP_0: TKeyCode = $62; 540 | /// 541 | /// Keypad Dot key 542 | /// 543 | KEY_KP_DOT: TKeyCode = $63; 544 | /// 545 | /// Non US Backslash key 546 | /// 547 | KEY_NONUS_BSLASH: TKeyCode = $64; 548 | /// 549 | /// Application key 550 | /// 551 | KEY_APPLICATION: TKeyCode = $65; 552 | /// 553 | /// Power key 554 | /// 555 | KEY_POWER: TKeyCode = $66; 556 | /// 557 | /// Keypad Equal key 558 | /// 559 | KEY_KP_EQUAL: TKeyCode = $67; 560 | 561 | const 562 | /// 563 | /// Media Next key 564 | /// 565 | MEDIA_NEXT: TMediaCode = $b5; 566 | /// 567 | /// Media Previous key 568 | /// 569 | MEDIA_PREV: TMediaCode = $b6; 570 | /// 571 | /// Media Stop key 572 | /// 573 | MEDIA_STOP: TMediaCode = $b7; 574 | /// 575 | /// Media Play key 576 | /// 577 | MEDIA_PLAY: TMediaCode = $cd; 578 | /// 579 | /// Media Mute key 580 | /// 581 | MEDIA_MUTE: TMediaCode = $e2; 582 | /// 583 | /// Media Volume Up key 584 | /// 585 | MEDIA_VOL_UP: TMediaCode = $ea; 586 | /// 587 | /// Media Volume Down key 588 | /// 589 | MEDIA_VOL_DN: TMediaCode = $e9; 590 | 591 | const 592 | /// 593 | /// Mouse Left button 594 | /// 595 | MOUSE_LEFT: TMouseKeyCode = $01; 596 | /// 597 | /// Mouse Right button 598 | /// 599 | MOUSE_RIGHT: TMouseKeyCode = $02; 600 | /// 601 | /// Mouse Center button 602 | /// 603 | MOUSE_CENTER: TMouseKeyCode = $04; 604 | 605 | const 606 | /// 607 | /// Mouse Wheel Click 608 | /// 609 | MOUSE_WHEEL_CLICK: TMouseWheelCode = $00; 610 | /// 611 | /// Mouse Wheel Up 612 | /// 613 | MOUSE_WHEEL_UP: TMouseWheelCode = $01; 614 | /// 615 | /// Mouse Wheel Down 616 | /// 617 | MOUSE_WHEEL_DOWN: TMouseWheelCode = $FF; 618 | 619 | const 620 | /// 621 | /// No modifier key pressed 622 | /// 623 | MOD_NOMOD: TModifierCode = $00; 624 | /// 625 | /// Left Control key 626 | /// 627 | MOD_CTRL: TModifierCode = $01; 628 | /// 629 | /// Left Shift key 630 | /// 631 | MOD_SHIFT: TModifierCode = $02; 632 | /// 633 | /// Left Alt key 634 | /// 635 | MOD_ALT: TModifierCode = $04; 636 | /// 637 | /// Left Win (Windows) key 638 | /// 639 | MOD_WIN: TModifierCode = $08; 640 | 641 | const 642 | MouseLeft = 'Left'; 643 | MouseMiddle = 'Middle'; 644 | MouseRight = 'Right'; 645 | WheelDown = 'Up'; 646 | WheelUp = 'Down'; 647 | 648 | const 649 | MediaPlayPause = 'PlayPause'; 650 | MediaPrevious = 'Previous'; 651 | MediaNext = 'Next'; 652 | MediaMute = 'Mute'; 653 | MediaVolumeUp = 'VolumeUp'; 654 | MediaVolumeDown = 'VolumeDown'; 655 | 656 | /// 657 | /// Create a Clear Key Macro 658 | /// 659 | /// Index of the key in the Macro Keyboard 660 | /// Macro as array of Bytes. 661 | function CreateClearKeyMacro(const MacroKey: Byte): THIDMacro; 662 | /// 663 | /// Create a Keyboard Key Macro 664 | /// 665 | /// Index of the key in the Macro Keyboard 666 | /// Key codes 667 | /// Modifier keys (Ctrl, Shift, Alt) 668 | /// Macro as array of Bytes. 669 | function CreateKeyboardKeyMacro(const MacroKey: Byte; const KeyCodes: TKeyCodes; const Modifiers: TModifierCodes): THIDMacro; 670 | /// 671 | /// Create a Mouse Key Macro 672 | /// 673 | /// Index of the key in the Macro Keyboard 674 | /// Mouse Key 675 | /// Macro as array of Bytes. 676 | function CreateMouseKeyMacro(const MacroKey: Byte; const MouseKey: TMouseKeyCode; const Modifiers: TModifierCodes): THIDMacro; 677 | /// 678 | /// Create a Mouse Wheel Macro 679 | /// 680 | /// Index of the key in the Macro Keyboard 681 | /// Wheel direction/click 682 | /// Modifier keys (Ctrl, Shift, Alt) 683 | /// Macro as array of Bytes. 684 | function CreateMouseWheelMacro(const MacroKey: Byte; const Wheel: TMouseWheelCode; const Modifiers: TModifierCodes): THIDMacro; 685 | /// 686 | /// Create a Media Macro 687 | /// 688 | /// Index of the key in the Macro Keyboard 689 | /// Media Key 690 | /// Macro as array of Bytes. 691 | function CreateMediaMacro(const MacroKey: Byte; const MediaKey: TMediaCode): THIDMacro; 692 | /// 693 | /// TExt to KeyCode 694 | /// 695 | /// Text representation of the keycode 696 | /// Keycode 697 | function TextToKeyCode(const Text: string): TKeyCode; 698 | 699 | implementation 700 | 701 | uses System.SysUtils; 702 | 703 | //------------------------------------------------------------------------------ 704 | // CREATE CLEAR KEY MACRO 705 | //------------------------------------------------------------------------------ 706 | function CreateClearKeyMacro(const MacroKey: Byte): THIDMacro; 707 | begin 708 | // Set length of the Macro command 709 | SetLength(Result, MACRO_DATA_LENGTH); 710 | // Fill the Macro command with zero's 711 | FillChar(Result[0], Length(Result) * SizeOf(Byte), 0); 712 | // Set the Macro Key Index 713 | Result[1] := MacroKey; 714 | end; 715 | 716 | //------------------------------------------------------------------------------ 717 | // CREATE KEYBOARD KEY MACRO 718 | //------------------------------------------------------------------------------ 719 | function CreateKeyboardKeyMacro(const MacroKey: Byte; const KeyCodes: TKeyCodes; const Modifiers: TModifierCodes): THIDMacro; 720 | var 721 | I: Integer; 722 | ModifiersCode: TModifierCode; 723 | begin 724 | // Set length of the Macro command 725 | SetLength(Result, MACRO_DATA_LENGTH); 726 | // Fill the Macro command with zero's 727 | FillChar(Result[0], Length(Result) * SizeOf(Byte), 0); 728 | // Set the Macro Key Index 729 | Result[1] := MacroKey; 730 | // Append the modifiers to a single value 731 | ModifiersCode := $00; 732 | for I := Low(Modifiers) to High(Modifiers) do ModifiersCode := ModifiersCode + Modifiers[I]; 733 | // Set the Modifier 734 | Result[2] := ModifiersCode; 735 | // Set Key 1 736 | if Length(KeyCodes) >= 1 then Result[4] := KeyCodes[0]; 737 | // Set Key 2 738 | if Length(KeyCodes) >= 2 then Result[5] := KeyCodes[1]; 739 | // Set Key 3 740 | if Length(KeyCodes) >= 3 then Result[6] := KeyCodes[2]; 741 | // Set Key 4 742 | if Length(KeyCodes) >= 4 then Result[7] := KeyCodes[3]; 743 | // Set Key 5 744 | if Length(KeyCodes) = 5 then Result[8] := KeyCodes[4]; 745 | // Set the Macro Type 746 | Result[9] := MACRO_KEY; 747 | end; 748 | 749 | //------------------------------------------------------------------------------ 750 | // CREATE MOUSE KEY MACRO 751 | //------------------------------------------------------------------------------ 752 | function CreateMouseKeyMacro(const MacroKey: Byte; const MouseKey: TMouseKeyCode; const Modifiers: TModifierCodes): THIDMacro; 753 | var 754 | I: Integer; 755 | ModifiersCode: TModifierCode; 756 | begin 757 | // Set length of the Macro command 758 | SetLength(Result, MACRO_DATA_LENGTH); 759 | // Fill the Macro command with zero's 760 | FillChar(Result[0], Length(Result) * SizeOf(Byte), 0); 761 | // Set the Macro Key Index 762 | Result[1] := MacroKey; 763 | // Append the modifiers to a single value 764 | ModifiersCode := $00; 765 | for I := Low(Modifiers) to High(Modifiers) do ModifiersCode := ModifiersCode + Modifiers[I]; 766 | // Set the Modifier 767 | Result[2] := ModifiersCode; 768 | // Set the Mouse Key 769 | Result[4] := MouseKey; 770 | // Set the Macro Type 771 | Result[9] := MACRO_MOUSE; 772 | end; 773 | 774 | //------------------------------------------------------------------------------ 775 | // CREATE MOUSE WHEEL MACRO 776 | //------------------------------------------------------------------------------ 777 | function CreateMouseWheelMacro(const MacroKey: Byte; const Wheel: TMouseWheelCode; const Modifiers: TModifierCodes): THIDMacro; 778 | var 779 | I: Integer; 780 | ModifiersCode: TModifierCode; 781 | begin 782 | // Set length of the Macro command 783 | SetLength(Result, MACRO_DATA_LENGTH); 784 | // Fill the Macro command with zero's 785 | FillChar(Result[0], Length(Result) * SizeOf(Byte), 0); 786 | // Set the Macro Key Index 787 | Result[1] := MacroKey; 788 | // Append the modifiers to a single value 789 | ModifiersCode := $00; 790 | for I := Low(Modifiers) to High(Modifiers) do ModifiersCode := ModifiersCode + Modifiers[I]; 791 | // Set the Modifier 792 | Result[2] := ModifiersCode; 793 | // Set the Mouse Wheel 794 | Result[6] := Wheel; 795 | // Set the Macro Type 796 | Result[9] := MACRO_MOUSE; 797 | end; 798 | 799 | //------------------------------------------------------------------------------ 800 | // CREATE MEDIA KEY MACRO 801 | //------------------------------------------------------------------------------ 802 | function CreateMediaMacro(const MacroKey: Byte; const MediaKey: TMediaCode): THIDMacro; 803 | begin 804 | // Set length of the Macro command 805 | SetLength(Result, MACRO_DATA_LENGTH); 806 | // Fill the Macro command with zero's 807 | FillChar(Result[0], Length(Result) * SizeOf(Byte), 0); 808 | // Set the Macro Key Index 809 | Result[1] := MacroKey; 810 | // Set the Media Key 811 | Result[2] := MediaKey; 812 | // Set the Macro Type 813 | Result[9] := MACRO_MEDIA; 814 | end; 815 | 816 | //------------------------------------------------------------------------------ 817 | // TEXT TO KEYCODE 818 | //------------------------------------------------------------------------------ 819 | function TextToKeyCode(const Text: string): TKeyCode; 820 | begin 821 | // Initialize result 822 | Result := KEY_NOKEY; 823 | 824 | // Alphabet (A-Z) 825 | if CompareText('A', Text) = 0 then Result := KEY_A; 826 | if CompareText('B', Text) = 0 then Result := KEY_B; 827 | if CompareText('C', Text) = 0 then Result := KEY_C; 828 | if CompareText('D', Text) = 0 then Result := KEY_D; 829 | if CompareText('E', Text) = 0 then Result := KEY_E; 830 | if CompareText('F', Text) = 0 then Result := KEY_F; 831 | if CompareText('G', Text) = 0 then Result := KEY_G; 832 | if CompareText('H', Text) = 0 then Result := KEY_H; 833 | if CompareText('I', Text) = 0 then Result := KEY_I; 834 | if CompareText('J', Text) = 0 then Result := KEY_J; 835 | if CompareText('K', Text) = 0 then Result := KEY_K; 836 | if CompareText('L', Text) = 0 then Result := KEY_L; 837 | if CompareText('M', Text) = 0 then Result := KEY_M; 838 | if CompareText('N', Text) = 0 then Result := KEY_N; 839 | if CompareText('O', Text) = 0 then Result := KEY_O; 840 | if CompareText('P', Text) = 0 then Result := KEY_P; 841 | if CompareText('Q', Text) = 0 then Result := KEY_Q; 842 | if CompareText('R', Text) = 0 then Result := KEY_R; 843 | if CompareText('S', Text) = 0 then Result := KEY_S; 844 | if CompareText('T', Text) = 0 then Result := KEY_T; 845 | if CompareText('U', Text) = 0 then Result := KEY_U; 846 | if CompareText('V', Text) = 0 then Result := KEY_V; 847 | if CompareText('W', Text) = 0 then Result := KEY_W; 848 | if CompareText('X', Text) = 0 then Result := KEY_X; 849 | if CompareText('Y', Text) = 0 then Result := KEY_Y; 850 | if CompareText('Z', Text) = 0 then Result := KEY_Z; 851 | 852 | // Numbers (1-0) 853 | if CompareText('1', Text) = 0 then Result := KEY_N1; 854 | if CompareText('2', Text) = 0 then Result := KEY_N2; 855 | if CompareText('3', Text) = 0 then Result := KEY_N3; 856 | if CompareText('4', Text) = 0 then Result := KEY_N4; 857 | if CompareText('5', Text) = 0 then Result := KEY_N5; 858 | if CompareText('6', Text) = 0 then Result := KEY_N6; 859 | if CompareText('7', Text) = 0 then Result := KEY_N7; 860 | if CompareText('8', Text) = 0 then Result := KEY_N8; 861 | if CompareText('9', Text) = 0 then Result := KEY_N9; 862 | if CompareText('0', Text) = 0 then Result := KEY_N0; 863 | 864 | // Others 865 | if CompareText('RETURN', Text) = 0 then Result := KEY_ENTER; 866 | if CompareText('ESC', Text) = 0 then Result := KEY_ESCAPE; 867 | if CompareText('BACKSPACE', Text) = 0 then Result := KEY_BSPACE; 868 | if CompareText('TAB', Text) = 0 then Result := KEY_TAB; 869 | if CompareText('SPACE', Text) = 0 then Result := KEY_SPACE; 870 | if CompareText('PRINT SCREEN', Text) = 0 then Result := KEY_PSCREEN; 871 | if CompareText('PAUSE', Text) = 0 then Result := KEY_PAUSE; 872 | if CompareText('INSERT', Text) = 0 then Result := KEY_INSERT; 873 | if CompareText('DELETE', Text) = 0 then Result := KEY_DELETE; 874 | if CompareText('HOME', Text) = 0 then Result := KEY_HOME; 875 | if CompareText('END', Text) = 0 then Result := KEY_END; 876 | if CompareText('PAGE UP', Text) = 0 then Result := KEY_PGUP; 877 | if CompareText('PAGE DOWN', Text) = 0 then Result := KEY_PGDOWN; 878 | 879 | // Special characters 880 | if CompareText('-', Text) = 0 then Result := KEY_MINUS; 881 | if CompareText('=', Text) = 0 then Result := KEY_EQUAL; 882 | if CompareText('[', Text) = 0 then Result := KEY_LBRACKET; 883 | if CompareText(']', Text) = 0 then Result := KEY_RBRACKET; 884 | if CompareText('\', Text) = 0 then Result := KEY_BSLASH; 885 | if CompareText('#', Text) = 0 then Result := KEY_NONUS_HASH; 886 | if CompareText(';', Text) = 0 then Result := KEY_SCOLON; 887 | if CompareText('"', Text) = 0 then Result := KEY_QUOTE; 888 | if CompareText('~', Text) = 0 then Result := KEY_GRAVE; 889 | if CompareText(',', Text) = 0 then Result := KEY_COMMA; 890 | if CompareText('.', Text) = 0 then Result := KEY_DOT; 891 | if CompareText('/', Text) = 0 then Result := KEY_SLASH; 892 | 893 | // Lock 894 | if CompareText('CAPS LOCK', Text) = 0 then Result := KEY_CAPSLOCK; 895 | if CompareText('SCROLL LOCK', Text) = 0 then Result := KEY_SCROLLLOCK; 896 | if CompareText('NUM LOCK', Text) = 0 then Result := KEY_NUMLOCK; 897 | 898 | // Function 899 | if CompareText('F1', Text) = 0 then Result := KEY_F1; 900 | if CompareText('F2', Text) = 0 then Result := KEY_F2; 901 | if CompareText('F3', Text) = 0 then Result := KEY_F3; 902 | if CompareText('F4', Text) = 0 then Result := KEY_F4; 903 | if CompareText('F5', Text) = 0 then Result := KEY_F5; 904 | if CompareText('F6', Text) = 0 then Result := KEY_F6; 905 | if CompareText('F7', Text) = 0 then Result := KEY_F7; 906 | if CompareText('F8', Text) = 0 then Result := KEY_F8; 907 | if CompareText('F9', Text) = 0 then Result := KEY_F9; 908 | if CompareText('F10', Text) = 0 then Result := KEY_F10; 909 | if CompareText('F11', Text) = 0 then Result := KEY_F11; 910 | if CompareText('F12', Text) = 0 then Result := KEY_F12; 911 | 912 | // Arrow 913 | if CompareText('UP', Text) = 0 then Result := KEY_UP; 914 | if CompareText('DOWN', Text) = 0 then Result := KEY_DOWN; 915 | if CompareText('LEFT', Text) = 0 then Result := KEY_LEFT; 916 | if CompareText('RIGHT', Text) = 0 then Result := KEY_RIGHT; 917 | 918 | // Numpad Numbers (1-0) 919 | if CompareText('NUMPAD 1', Text) = 0 then Result := KEY_KP_1; 920 | if CompareText('NUMPAD 2', Text) = 0 then Result := KEY_KP_2; 921 | if CompareText('NUMPAD 3', Text) = 0 then Result := KEY_KP_3; 922 | if CompareText('NUMPAD 4', Text) = 0 then Result := KEY_KP_4; 923 | if CompareText('NUMPAD 5', Text) = 0 then Result := KEY_KP_5; 924 | if CompareText('NUMPAD 6', Text) = 0 then Result := KEY_KP_6; 925 | if CompareText('NUMPAD 7', Text) = 0 then Result := KEY_KP_7; 926 | if CompareText('NUMPAD 8', Text) = 0 then Result := KEY_KP_8; 927 | if CompareText('NUMPAD 9', Text) = 0 then Result := KEY_KP_9; 928 | if CompareText('NUMPAD 0', Text) = 0 then Result := KEY_KP_0; 929 | 930 | // Numpad Others 931 | if CompareText('NUMPAD *', Text) = 0 then Result := KEY_KP_ASTERISK; 932 | if CompareText('NUMPAD +', Text) = 0 then Result := KEY_KP_PLUS; 933 | if CompareText('NUMPAD ,', Text) = 0 then Result := KEY_KP_0; 934 | if CompareText('NUMPAD -', Text) = 0 then Result := KEY_KP_MINUS; 935 | if CompareText('NUMPAD .', Text) = 0 then Result := KEY_KP_DOT; 936 | if CompareText('NUMPAD /', Text) = 0 then Result := KEY_KP_SLASH; 937 | if CompareText('NUMPAD =', Text) = 0 then Result := KEY_KP_EQUAL; 938 | if CompareText('NUMPAD RETURN', Text) = 0 then Result := KEY_KP_ENTER; 939 | if CompareText('NUMPAD POWER', Text) = 0 then Result := KEY_POWER; 940 | end; 941 | 942 | end. 943 | 944 | -------------------------------------------------------------------------------- /Units/HID.MacroKeyboard.Config.pas: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // UNIT : HID.MacroKeyboard.Config.pas 3 | // CONTENTS : HID MacroKeyboard Configuration 4 | // VERSION : 1.0 5 | // TARGET : Embarcadero Delphi 11 or higher 6 | // AUTHOR : Ernst Reidinga (ERDesigns) 7 | // STATUS : Open source under Apache 2.0 library 8 | // COMPATIBILITY : Windows 7, 8/8.1, 10, 11 9 | // RELEASE DATE : 21/05/2024 10 | //------------------------------------------------------------------------------ 11 | unit HID.MacroKeyboard.Config; 12 | 13 | interface 14 | 15 | uses 16 | System.SysUtils, System.Classes, System.JSON, System.IOUtils, Vcl.Menus, 17 | WinApi.Windows, System.Generics.Collections, HID, HID.MacroKeyboard; 18 | 19 | type 20 | TMacroKey = class(TCollectionItem) 21 | private 22 | /// 23 | /// Macro Key name (Friendly name as indicator / reference) 24 | /// 25 | FName: string; 26 | /// 27 | /// Macro Type (0 = Keyboard, 1 = Mouse, 2 = Media) 28 | /// 29 | FType: Integer; 30 | /// 31 | /// Ctrl Modifier 32 | /// 33 | FCtrl: Boolean; 34 | /// 35 | /// Shift Modifier 36 | /// 37 | FShift: Boolean; 38 | /// 39 | /// Alt Modifier 40 | /// 41 | FAlt: Boolean; 42 | /// 43 | /// Win Modifier 44 | /// 45 | FWin: Boolean; 46 | /// 47 | /// Mouse 48 | /// 49 | FMouse: string; 50 | /// 51 | /// Media 52 | /// 53 | FMedia: string; 54 | /// 55 | /// Key 1 56 | /// 57 | FKey1: string; 58 | /// 59 | /// Key 2 60 | /// 61 | FKey2: string; 62 | /// 63 | /// Key 3 64 | /// 65 | FKey3: string; 66 | /// 67 | /// Key 4 68 | /// 69 | FKey4: string; 70 | /// 71 | /// Key 5 72 | /// 73 | FKey5: string; 74 | 75 | /// 76 | /// Set Macro Key name 77 | /// 78 | procedure SetName(const Name: string); 79 | /// 80 | /// Set Macro Type 81 | /// 82 | procedure SetType(const &Type: Integer); 83 | /// 84 | /// Set Ctrl Modifier 85 | /// 86 | procedure SetCtrl(const Ctrl: Boolean); 87 | /// 88 | /// Set Shift Modifier 89 | /// 90 | procedure SetShift(const Shift: Boolean); 91 | /// 92 | /// Set Alt Modifier 93 | /// 94 | procedure SetAlt(const Alt: Boolean); 95 | /// 96 | /// Set Win Modifier 97 | /// 98 | procedure SetWin(const Win: Boolean); 99 | /// 100 | /// Set Mouse 101 | /// 102 | procedure SetMouse(const Mouse: string); 103 | /// 104 | /// Set Media 105 | /// 106 | procedure SetMedia(const Media: string); 107 | /// 108 | /// Set Key 1 109 | /// 110 | procedure SetKey1(const Key: string); 111 | /// 112 | /// Set Key 2 113 | /// 114 | procedure SetKey2(const Key: string); 115 | /// 116 | /// Set Key 3 117 | /// 118 | procedure SetKey3(const Key: string); 119 | /// 120 | /// Set Key 4 121 | /// 122 | procedure SetKey4(const Key: string); 123 | /// 124 | /// Set Key 5 125 | /// 126 | procedure SetKey5(const Key: string); 127 | protected 128 | /// 129 | /// Get Display name 130 | /// 131 | function GetDisplayName: string; override; 132 | public 133 | /// 134 | /// Constructor 135 | /// 136 | constructor Create(AOWner: TCollection); override; 137 | /// 138 | /// Destructor 139 | /// 140 | destructor Destroy; override; 141 | 142 | /// 143 | /// Assign 144 | /// 145 | procedure Assign(Source: TPersistent); override; 146 | 147 | /// 148 | /// To HID Macro 149 | /// 150 | function ToHIDMacro(const MacroKey: Byte): THIDMacro; 151 | published 152 | /// 153 | /// Macro Key name (Friendly name as indicator / reference) 154 | /// 155 | property Name: string read FName write SetName; 156 | /// 157 | /// Macro Type (0 = Keyboard, 1 = Mouse, 2 = Media) 158 | /// 159 | property &Type: Integer read FType write SetType default 0; 160 | /// 161 | /// Ctrl Modifier 162 | /// 163 | property Ctrl: Boolean read FCtrl write SetCtrl; 164 | /// 165 | /// Shift Modifier 166 | /// 167 | property Shift: Boolean read FShift write SetShift; 168 | /// 169 | /// Alt Modifier 170 | /// 171 | property Alt: Boolean read FAlt write SetAlt; 172 | /// 173 | /// Win Modifier 174 | /// 175 | property Win: Boolean read FWin write SetWin; 176 | /// 177 | /// Mouse 178 | /// 179 | property Mouse: string read FMouse write SetMouse; 180 | /// 181 | /// Media 182 | /// 183 | property Media: string read FMedia write SetMedia; 184 | /// 185 | /// Key 1 186 | /// 187 | property Key1: string read FKey1 write SetKey1; 188 | /// 189 | /// Key 2 190 | /// 191 | property Key2: string read FKey2 write SetKey2; 192 | /// 193 | /// Key 3 194 | /// 195 | property Key3: string read FKey3 write SetKey3; 196 | /// 197 | /// Key 4 198 | /// 199 | property Key4: string read FKey4 write SetKey4; 200 | /// 201 | /// Key 5 202 | /// 203 | property Key5: string read FKey5 write SetKey5; 204 | end; 205 | 206 | TMacroKeys = class(TOwnedCollection) 207 | private 208 | /// 209 | /// On Change Event 210 | /// 211 | FOnChange : TNotifyEvent; 212 | 213 | /// 214 | /// Item Changed handler 215 | /// 216 | procedure ItemChanged(Sender: TObject); 217 | 218 | /// 219 | /// Get Macro Key Item 220 | /// 221 | function GetItem(Index: Integer): TMacroKey; 222 | /// 223 | /// Set Macro Key Item 224 | /// 225 | procedure SetItem(Index: Integer; const Value: TMacroKey); 226 | protected 227 | /// 228 | /// Update 229 | /// 230 | procedure Update(Item: TCollectionItem); override; 231 | public 232 | /// 233 | /// Constructor 234 | /// 235 | constructor Create(AOwner: TPersistent); 236 | 237 | /// 238 | /// Add Macro Key 239 | /// 240 | function Add: TMacroKey; 241 | /// 242 | /// Assign 243 | /// 244 | procedure Assign(Source: TPersistent); override; 245 | 246 | /// 247 | /// Macro Key Items 248 | /// 249 | property Items[Index: Integer]: TMacroKey read GetItem write SetItem; default; 250 | /// 251 | /// On Change Event 252 | /// 253 | property OnChange: TNotifyEvent read FOnChange write FOnChange; 254 | end; 255 | 256 | TMacroKeyboardFilenameEvent = procedure(Sender: TObject; Filename: string) of object; 257 | TMacroKeyboardModifiedEvent = procedure(Sender: TObject; Modified: Boolean) of object; 258 | 259 | TMacroKeyboardConfig = class(TComponent) 260 | private 261 | /// 262 | /// Configuration filename 263 | /// 264 | FFileName: string; 265 | /// 266 | /// Modified flag 267 | /// 268 | FModified: Boolean; 269 | /// 270 | /// Macro key collection 271 | /// 272 | FKeys: TMacroKeys; 273 | 274 | /// 275 | /// Filename change event 276 | /// 277 | FOnFilename: TMacroKeyboardFilenameEvent; 278 | /// 279 | /// Modified changed event 280 | /// 281 | FOnModified: TMacroKeyboardModifiedEvent; 282 | 283 | /// 284 | /// Keys changed event handler 285 | /// 286 | procedure KeysChanged(Sender: TObject); 287 | /// 288 | /// Set filename 289 | /// 290 | procedure SetFileName(const Filename: string); 291 | /// 292 | /// Set modified flag 293 | /// 294 | procedure SetModified(const Modified: Boolean); 295 | public 296 | /// 297 | /// Constructor 298 | /// 299 | constructor Create(AOwner: TComponent); override; 300 | /// 301 | /// Destructor 302 | /// 303 | destructor Destroy; override; 304 | 305 | /// 306 | /// Assign 307 | /// 308 | procedure Assign(Source: TPersistent); override; 309 | 310 | /// 311 | /// New config 312 | /// 313 | procedure New; 314 | /// 315 | /// Load from file 316 | /// 317 | procedure LoadFromFile(const FileName: string); 318 | /// 319 | /// Save to file 320 | /// 321 | procedure SaveToFile(const Filename: string); 322 | published 323 | /// 324 | /// Configuration filename 325 | /// 326 | property FileName: string read FFileName write SetFileName; 327 | /// 328 | /// Modified flag 329 | /// 330 | property Modified: Boolean read FModified write SetModified; 331 | /// 332 | /// Macro key collection 333 | /// 334 | property Keys: TMacroKeys read FKeys; 335 | 336 | /// 337 | /// Filename change event 338 | /// 339 | property OnFilename: TMacroKeyboardFilenameEvent read FOnFilename write FOnFilename; 340 | /// 341 | /// Modified changed event 342 | /// 343 | property OnModified: TMacroKeyboardModifiedEvent read FOnModified write FOnModified; 344 | end; 345 | 346 | implementation 347 | 348 | uses System.StrUtils; 349 | 350 | //------------------------------------------------------------------------------ 351 | // SET NAME 352 | //------------------------------------------------------------------------------ 353 | procedure TMacroKey.SetName(const Name: string); 354 | begin 355 | if Name <> FName then 356 | begin 357 | FName := Name; 358 | Changed(False); 359 | end; 360 | end; 361 | 362 | //------------------------------------------------------------------------------ 363 | // SET TYPE 364 | //------------------------------------------------------------------------------ 365 | procedure TMacroKey.SetType(const &Type: Integer); 366 | begin 367 | if &Type <> FType then 368 | begin 369 | FType := &Type; 370 | Changed(False); 371 | end; 372 | end; 373 | 374 | //------------------------------------------------------------------------------ 375 | // SET CTRL 376 | //------------------------------------------------------------------------------ 377 | procedure TMacroKey.SetCtrl(const Ctrl: Boolean); 378 | begin 379 | if Ctrl <> FCtrl then 380 | begin 381 | FCtrl := Ctrl; 382 | Changed(False); 383 | end; 384 | end; 385 | 386 | //------------------------------------------------------------------------------ 387 | // SET SHIFT 388 | //------------------------------------------------------------------------------ 389 | procedure TMacroKey.SetShift(const Shift: Boolean); 390 | begin 391 | if Shift <> FShift then 392 | begin 393 | FShift := Shift; 394 | Changed(False); 395 | end; 396 | end; 397 | 398 | //------------------------------------------------------------------------------ 399 | // SET ALT 400 | //------------------------------------------------------------------------------ 401 | procedure TMacroKey.SetAlt(const Alt: Boolean); 402 | begin 403 | if Alt <> FAlt then 404 | begin 405 | FAlt := Alt; 406 | Changed(False); 407 | end; 408 | end; 409 | 410 | //------------------------------------------------------------------------------ 411 | // SET WIN 412 | //------------------------------------------------------------------------------ 413 | procedure TMacroKey.SetWin(const Win: Boolean); 414 | begin 415 | if Win <> FWin then 416 | begin 417 | FWin := Win; 418 | Changed(False); 419 | end; 420 | end; 421 | 422 | //------------------------------------------------------------------------------ 423 | // SET MOUSE 424 | //------------------------------------------------------------------------------ 425 | procedure TMacroKey.SetMouse(const Mouse: string); 426 | begin 427 | if Mouse <> FMouse then 428 | begin 429 | FMouse := Mouse; 430 | Changed(False); 431 | end; 432 | end; 433 | 434 | //------------------------------------------------------------------------------ 435 | // SET MEDIA 436 | //------------------------------------------------------------------------------ 437 | procedure TMacroKey.SetMedia(const Media: string); 438 | begin 439 | if Media <> FMedia then 440 | begin 441 | FMedia := Media; 442 | Changed(False); 443 | end; 444 | end; 445 | 446 | //------------------------------------------------------------------------------ 447 | // SET KEY 1 448 | //------------------------------------------------------------------------------ 449 | procedure TMacroKey.SetKey1(const Key: string); 450 | begin 451 | if Key <> FKey1 then 452 | begin 453 | FKey1 := Key; 454 | Changed(False); 455 | end; 456 | end; 457 | 458 | //------------------------------------------------------------------------------ 459 | // SET KEY 2 460 | //------------------------------------------------------------------------------ 461 | procedure TMacroKey.SetKey2(const Key: string); 462 | begin 463 | if Key <> FKey2 then 464 | begin 465 | FKey2 := Key; 466 | Changed(False); 467 | end; 468 | end; 469 | 470 | //------------------------------------------------------------------------------ 471 | // SET KEY 3 472 | //------------------------------------------------------------------------------ 473 | procedure TMacroKey.SetKey3(const Key: string); 474 | begin 475 | if Key <> FKey3 then 476 | begin 477 | FKey3 := Key; 478 | Changed(False); 479 | end; 480 | end; 481 | 482 | //------------------------------------------------------------------------------ 483 | // SET KEY 4 484 | //------------------------------------------------------------------------------ 485 | procedure TMacroKey.SetKey4(const Key: string); 486 | begin 487 | if Key <> FKey4 then 488 | begin 489 | FKey4 := Key; 490 | Changed(False); 491 | end; 492 | end; 493 | 494 | //------------------------------------------------------------------------------ 495 | // SET KEY 5 496 | //------------------------------------------------------------------------------ 497 | procedure TMacroKey.SetKey5(const Key: string); 498 | begin 499 | if Key <> FKey5 then 500 | begin 501 | FKey5 := Key; 502 | Changed(False); 503 | end; 504 | end; 505 | 506 | //------------------------------------------------------------------------------ 507 | // GET DISPLAY NAME 508 | //------------------------------------------------------------------------------ 509 | function TMacroKey.GetDisplayName: string; 510 | var 511 | S: string; 512 | begin 513 | if (Name <> '') then 514 | Result := Name 515 | else 516 | begin 517 | if Ctrl then S := S + 'CTRL '; 518 | if Shift then S := S + 'SHIFT '; 519 | if Alt then S := S + 'ALT '; 520 | if Win then S := S + 'WIN '; 521 | if (Mouse <> '') then S := S + Mouse + ' '; 522 | if (Media <> '') then S := S + Media + ' '; 523 | if (Key1 <> '') then S := S + Key1 + ' '; 524 | if (Key2 <> '') then S := S + Key2 + ' '; 525 | if (Key3 <> '') then S := S + Key3 + ' '; 526 | if (Key4 <> '') then S := S + Key4 + ' '; 527 | if (Key5 <> '') then S := S + Key5 + ' '; 528 | Result := Trim(S); 529 | end; 530 | end; 531 | 532 | //------------------------------------------------------------------------------ 533 | // CONSTRUCTOR 534 | //------------------------------------------------------------------------------ 535 | constructor TMacroKey.Create(AOWner: TCollection); 536 | begin 537 | inherited Create(AOwner); 538 | end; 539 | 540 | //------------------------------------------------------------------------------ 541 | // DESTRUCTOR 542 | //------------------------------------------------------------------------------ 543 | destructor TMacroKey.Destroy; 544 | begin 545 | inherited Destroy; 546 | end; 547 | 548 | //------------------------------------------------------------------------------ 549 | // ASSIGN 550 | //------------------------------------------------------------------------------ 551 | procedure TMacroKey.Assign(Source: TPersistent); 552 | begin 553 | if (Source is TMacroKey) then 554 | begin 555 | FName := (Source as TMacroKey).Name; 556 | FType := (Source as TMacroKey).&Type; 557 | FCtrl := (Source as TMacroKey).Ctrl; 558 | FShift := (Source as TMacroKey).Shift; 559 | FAlt := (Source as TMacroKey).Alt; 560 | FWin := (Source as TMacroKey).Win; 561 | FMouse := (Source as TMacroKey).Mouse; 562 | FMedia := (Source as TMacroKey).Media; 563 | FKey1 := (Source as TMacroKey).Key1; 564 | FKey2 := (Source as TMacroKey).Key2; 565 | FKey3 := (Source as TMacroKey).Key3; 566 | FKey4 := (Source as TMacroKey).Key4; 567 | FKey5 := (Source as TMacroKey).Key5; 568 | end; 569 | end; 570 | 571 | //------------------------------------------------------------------------------ 572 | // TO HID MACRO 573 | //------------------------------------------------------------------------------ 574 | function TMacroKey.ToHIDMacro(const MacroKey: Byte): THIDMacro; 575 | 576 | function CtrlModifier: TModifierCode; 577 | begin 578 | if Ctrl then Result := MOD_CTRL else Result := MOD_NOMOD; 579 | end; 580 | 581 | function AltModifier: TModifierCode; 582 | begin 583 | if Alt then Result := MOD_ALT else Result := MOD_NOMOD; 584 | end; 585 | 586 | function ShiftModifier: TModifierCode; 587 | begin 588 | if Shift then Result := MOD_SHIFT else Result := MOD_NOMOD; 589 | end; 590 | 591 | function WinModifier: TModifierCode; 592 | begin 593 | if Win then Result := MOD_WIN else Result := MOD_NOMOD; 594 | end; 595 | 596 | begin 597 | case &Type of 598 | // Keyboard 599 | 0: begin 600 | Result := CreateKeyboardKeyMacro(MacroKey, [TextToKeyCode(Key1), TextToKeyCode(Key2), TextToKeyCode(Key3), TextToKeyCode(Key4), TextToKeyCode(Key5)], [CtrlModifier, AltModifier, ShiftModifier, WinModifier]); 601 | end; 602 | // Mouse 603 | 1: begin 604 | // Buttons 605 | if (Mouse = MouseLeft) then Result := CreateMouseKeyMacro(MacroKey, MOUSE_LEFT, [CtrlModifier, AltModifier, ShiftModifier, WinModifier]); 606 | if (Mouse = MouseMiddle) then Result := CreateMouseKeyMacro(MacroKey, MOUSE_CENTER, [CtrlModifier, AltModifier, ShiftModifier, WinModifier]); 607 | if (Mouse = MouseRight) then Result := CreateMouseKeyMacro(MacroKey, MOUSE_RIGHT, [CtrlModifier, AltModifier, ShiftModifier, WinModifier]); 608 | // Wheel 609 | if (Mouse = WheelDown) then Result := CreateMouseWheelMacro(MacroKey, MOUSE_WHEEL_DOWN, [CtrlModifier, AltModifier, ShiftModifier, WinModifier]); 610 | if (Mouse = WheelUp) then Result := CreateMouseWheelMacro(MacroKey, MOUSE_WHEEL_UP, [CtrlModifier, AltModifier, ShiftModifier, WinModifier]); 611 | end; 612 | // Media 613 | 2: begin 614 | if (Media = MediaPlayPause) then Result := CreateMediaMacro(MacroKey, MEDIA_PLAY); 615 | if (Media = MediaPrevious) then Result := CreateMediaMacro(MacroKey, MEDIA_PREV); 616 | if (Media = MediaNext) then Result := CreateMediaMacro(MacroKey, MEDIA_NEXT); 617 | if (Media = MediaVolumeUp) then Result := CreateMediaMacro(MacroKey, MEDIA_VOL_UP); 618 | if (Media = MediaVolumeDown) then Result := CreateMediaMacro(MacroKey, MEDIA_VOL_DN); 619 | if (Media = MediaMute) then Result := CreateMediaMacro(MacroKey, MEDIA_MUTE); 620 | end; 621 | end; 622 | end; 623 | 624 | //------------------------------------------------------------------------------ 625 | // ITEM CHANGED 626 | //------------------------------------------------------------------------------ 627 | procedure TMacroKeys.ItemChanged(Sender: TObject); 628 | begin 629 | // Notify item changed 630 | if Assigned(FOnChange) then FOnChange(Self); 631 | end; 632 | 633 | //------------------------------------------------------------------------------ 634 | // GET ITEM 635 | //------------------------------------------------------------------------------ 636 | function TMacroKeys.GetItem(Index: Integer): TMacroKey; 637 | begin 638 | Result := inherited GetItem(Index) as TMacroKey; 639 | end; 640 | 641 | //------------------------------------------------------------------------------ 642 | // SET ITEM 643 | //------------------------------------------------------------------------------ 644 | procedure TMacroKeys.SetItem(Index: Integer; const Value: TMacroKey); 645 | begin 646 | inherited SetItem(Index, Value); 647 | // Notify change 648 | ItemChanged(Self); 649 | end; 650 | 651 | //------------------------------------------------------------------------------ 652 | // UPDATE 653 | //------------------------------------------------------------------------------ 654 | procedure TMacroKeys.Update(Item: TCollectionItem); 655 | begin 656 | inherited Update(Item); 657 | // Notify change 658 | if Assigned(FOnChange) then FOnChange(Self); 659 | end; 660 | 661 | //------------------------------------------------------------------------------ 662 | // CONSTRUCTOR 663 | //------------------------------------------------------------------------------ 664 | constructor TMacroKeys.Create(AOwner: TPersistent); 665 | begin 666 | inherited Create(AOwner, TMacroKey); 667 | end; 668 | 669 | //------------------------------------------------------------------------------ 670 | // ADD 671 | //------------------------------------------------------------------------------ 672 | function TMacroKeys.Add: TMacroKey; 673 | begin 674 | Result := TMacroKey(inherited Add); 675 | end; 676 | 677 | //------------------------------------------------------------------------------ 678 | // ASSIGN 679 | //------------------------------------------------------------------------------ 680 | procedure TMacroKeys.Assign(Source: TPersistent); 681 | var 682 | Items: TMacroKeys; 683 | I: Integer; 684 | begin 685 | inherited; 686 | // Assign items 687 | if (Source is TMacroKeys) then 688 | begin 689 | // Assign Items 690 | Items := TMacroKeys(Source); 691 | // Clear all items 692 | Clear; 693 | // Loop over every item and add them to the collection 694 | for I := 0 to Items.Count - 1 do Add.Assign(Items.Items[I]); 695 | end; 696 | end; 697 | 698 | //------------------------------------------------------------------------------ 699 | // KEYS CHANGED 700 | //------------------------------------------------------------------------------ 701 | procedure TMacroKeyboardConfig.KeysChanged(Sender: TObject); 702 | begin 703 | FModified := True; 704 | end; 705 | 706 | //------------------------------------------------------------------------------ 707 | // SET FILENAME 708 | //------------------------------------------------------------------------------ 709 | procedure TMacroKeyboardConfig.SetFileName(const Filename: string); 710 | begin 711 | if (Filename <> FFilename) then 712 | begin 713 | // Update filename 714 | FFilename := Filename; 715 | // Notify change 716 | if Assigned(FOnFilename) then FOnFilename(Self, Filename); 717 | end; 718 | end; 719 | 720 | //------------------------------------------------------------------------------ 721 | // SET MODIFIED 722 | //------------------------------------------------------------------------------ 723 | procedure TMacroKeyboardConfig.SetModified(const Modified: Boolean); 724 | begin 725 | if (Modified <> FModified) then 726 | begin 727 | // Update modified flag 728 | FModified := Modified; 729 | // Notify change 730 | if Assigned(FOnModified) then FOnModified(Self, Modified); 731 | end; 732 | end; 733 | 734 | //------------------------------------------------------------------------------ 735 | // CONSTRUCTOR 736 | //------------------------------------------------------------------------------ 737 | constructor TMacroKeyboardConfig.Create(AOwner: TComponent); 738 | begin 739 | inherited Create(AOwner); 740 | 741 | // Create macro keys collection 742 | FKeys := TMacroKeys.Create(Self); 743 | // Set change handler 744 | FKeys.OnChange := KeysChanged; 745 | // Create new config if not in design mode 746 | if not (csDesigning in ComponentState) then New; 747 | end; 748 | 749 | //------------------------------------------------------------------------------ 750 | // DESTRUCTOR 751 | //------------------------------------------------------------------------------ 752 | destructor TMacroKeyboardConfig.Destroy; 753 | begin 754 | // Free macro keys collection 755 | FKeys.Free; 756 | 757 | inherited Destroy; 758 | end; 759 | 760 | //------------------------------------------------------------------------------ 761 | // ASSIGN 762 | //------------------------------------------------------------------------------ 763 | procedure TMacroKeyboardConfig.Assign(Source: TPersistent); 764 | begin 765 | inherited; 766 | 767 | // Assign properties 768 | if (Source is TMacroKeyboardConfig) then 769 | begin 770 | FKeys.Assign((Source as TMacroKeyboardConfig).Keys); 771 | FFilename := (Source as TMacroKeyboardConfig).FileName; 772 | FModified := (Source as TMacroKeyboardConfig).Modified; 773 | end; 774 | end; 775 | 776 | //------------------------------------------------------------------------------ 777 | // NEW CONFIG 778 | //------------------------------------------------------------------------------ 779 | procedure TMacroKeyboardConfig.New; 780 | var 781 | I: Integer; 782 | begin 783 | // Clear macro keys 784 | FKeys.Clear; 785 | 786 | // Add keys 787 | for I := 1 to 12 do with FKeys.Add do Name := Format('Key %d', [I]); 788 | 789 | // Add knobs 790 | for I := 1 to 3 do 791 | begin 792 | // Clockwise 793 | with FKeys.Add do Name := Format('Knob %d (Clockwise)', [I]); 794 | // Counter clockwise 795 | with FKeys.Add do Name := Format('Knob %d (Counter Clockwise)', [I]); 796 | // Click 797 | with FKeys.Add do Name := Format('Knob %d (Click)', [I]); 798 | end; 799 | 800 | // Update modified flag 801 | Modified := False; 802 | // Clear filename 803 | Filename := ''; 804 | end; 805 | 806 | //------------------------------------------------------------------------------ 807 | // LOAD FROM FILE 808 | //------------------------------------------------------------------------------ 809 | procedure TMacroKeyboardConfig.LoadFromFile(const FileName: string); 810 | var 811 | JSON: string; 812 | JSONArray: TJSONArray; 813 | JSONObject: TJSONObject; 814 | MacroKey: TMacroKey; 815 | I: Integer; 816 | begin 817 | // Make sure the file exists 818 | if not FileExists(Filename) then Exit; 819 | // Read the configuration file 820 | JSON := TFile.ReadAllText(FileName); 821 | // Parse the JSON 822 | JSONArray := TJSONObject.ParseJSONValue(JSON) as TJSONArray; 823 | try 824 | // Clear the macro keys 825 | FKeys.Clear; 826 | // Loop over the items in the configuration 827 | for I := 0 to JSONArray.Count - 1 do 828 | begin 829 | JSONObject := JSONArray.Items[i] as TJSONObject; 830 | MacroKey := FKeys.Add; 831 | MacroKey.Name := JSONObject.GetValue('Name'); 832 | MacroKey.&Type := JSONObject.GetValue('Type'); 833 | MacroKey.Ctrl := JSONObject.GetValue('Ctrl'); 834 | MacroKey.Shift := JSONObject.GetValue('Shift'); 835 | MacroKey.Alt := JSONObject.GetValue('Alt'); 836 | MacroKey.Win := JSONObject.GetValue('Win'); 837 | MacroKey.Mouse := JSONObject.GetValue('Mouse'); 838 | MacroKey.Media := JSONObject.GetValue('Media'); 839 | MacroKey.Key1 := JSONObject.GetValue('Key1'); 840 | MacroKey.Key2 := JSONObject.GetValue('Key2'); 841 | MacroKey.Key3 := JSONObject.GetValue('Key3'); 842 | MacroKey.Key4 := JSONObject.GetValue('Key4'); 843 | MacroKey.Key5 := JSONObject.GetValue('Key5'); 844 | end; 845 | // Update the filename 846 | Self.FileName := FileName; 847 | // Update the modified flag 848 | Modified := False; 849 | finally 850 | JSONArray.Free; 851 | end; 852 | end; 853 | 854 | //------------------------------------------------------------------------------ 855 | // SAVE TO FILE 856 | //------------------------------------------------------------------------------ 857 | procedure TMacroKeyboardConfig.SaveToFile(const Filename: string); 858 | var 859 | JSONArray: TJSONArray; 860 | JSONObject: TJSONObject; 861 | I: Integer; 862 | MacroKey: TMacroKey; 863 | JSONString: string; 864 | begin 865 | // Create a JSON array 866 | JSONArray := TJSONArray.Create; 867 | try 868 | // Loop over the macro keys 869 | for I := 0 to FKeys.Count - 1 do 870 | begin 871 | MacroKey := FKeys[I]; 872 | JSONObject := TJSONObject.Create; 873 | JSONObject.AddPair('Name', MacroKey.Name); 874 | JSONObject.AddPair('Type', TJSONNumber.Create(MacroKey.&Type)); 875 | JSONObject.AddPair('Ctrl', TJSONBool.Create(MacroKey.Ctrl)); 876 | JSONObject.AddPair('Shift', TJSONBool.Create(MacroKey.Shift)); 877 | JSONObject.AddPair('Alt', TJSONBool.Create(MacroKey.Alt)); 878 | JSONObject.AddPair('Win', TJSONBool.Create(MacroKey.Win)); 879 | JSONObject.AddPair('Mouse', MacroKey.Mouse); 880 | JSONObject.AddPair('Media', MacroKey.Media); 881 | JSONObject.AddPair('Key1', MacroKey.Key1); 882 | JSONObject.AddPair('Key2', MacroKey.Key2); 883 | JSONObject.AddPair('Key3', MacroKey.Key3); 884 | JSONObject.AddPair('Key4', MacroKey.Key4); 885 | JSONObject.AddPair('Key5', MacroKey.Key5); 886 | JSONArray.AddElement(JSONObject); 887 | end; 888 | // Conver the JSON array to a string 889 | JSONString := JSONArray.ToString; 890 | // Write the JSON string to the configuration file 891 | TFile.WriteAllText(FileName, JSONString); 892 | // Update the filename 893 | Self.Filename := Filename; 894 | // Update the modified flag 895 | Modified := False; 896 | finally 897 | JSONArray.Free; 898 | end; 899 | end; 900 | 901 | end. 902 | -------------------------------------------------------------------------------- /Units/HID.pas: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // UNIT : HID.pas 3 | // CONTENTS : HID Device Class 4 | // VERSION : 1.0 5 | // TARGET : Embarcadero Delphi 11 or higher 6 | // AUTHOR : Ernst Reidinga (ERDesigns) 7 | // STATUS : Open source under Apache 2.0 library 8 | // COMPATIBILITY : Windows 7, 8/8.1, 10, 11 9 | // RELEASE DATE : 19/05/2024 10 | //------------------------------------------------------------------------------ 11 | unit HID; 12 | 13 | interface 14 | 15 | uses 16 | WinApi.Windows, System.SysUtils, System.Classes, WinApi.Messages, Vcl.Forms, 17 | HID.Types, HID.Constants, dialogs; 18 | 19 | type 20 | THIDMacro = array of Byte; 21 | 22 | /// 23 | /// HID Device Class. 24 | /// 25 | THIDDevice = class 26 | private 27 | /// 28 | /// Path to the HID device. 29 | /// 30 | FDevicePath: string; 31 | /// 32 | /// Description of the HID device. 33 | /// 34 | FDescription: string; 35 | /// 36 | /// Manufacturer of the HID device. 37 | /// 38 | FManufacturer: string; 39 | /// 40 | /// Friendly name of the HID device. 41 | /// 42 | FFriendlyName: string; 43 | /// 44 | /// Location information of the HID device. 45 | /// 46 | FLocation: string; 47 | /// 48 | /// Hardware ID of the HID device. 49 | /// 50 | FHardwareID: string; 51 | /// 52 | /// Compatible IDs of the HID device. 53 | /// 54 | FCompatibleIDs: string; 55 | /// 56 | /// Service associated with the HID device. 57 | /// 58 | FService: string; 59 | /// 60 | /// Device class of the HID device. 61 | /// 62 | FDeviceClass: string; 63 | /// 64 | /// Class GUID of the HID device. 65 | /// 66 | FClassGUID: string; 67 | /// 68 | /// Driver associated with the HID device. 69 | /// 70 | FDriver: string; 71 | /// 72 | /// Physical device object name of the HID device. 73 | /// 74 | FPhysicalDeviceObjectName: string; 75 | /// 76 | /// Capabilities of the HID device. 77 | /// 78 | FCapabilities: string; 79 | /// 80 | /// UI number of the HID device. 81 | /// 82 | FUIDNumber: string; 83 | /// 84 | /// Manufacturer string from the HID device. 85 | /// 86 | FManufacturerString: string; 87 | /// 88 | /// Product string from the HID device. 89 | /// 90 | FProductString: string; 91 | /// 92 | /// Serial number string from the HID device. 93 | /// 94 | FSerialNumberString: string; 95 | /// 96 | /// Number of input buttons on the HID device. 97 | /// 98 | FNumberInputButtons: WORD; 99 | /// 100 | /// Number of output buttons on the HID device. 101 | /// 102 | FNumberOutputButtons: WORD; 103 | /// 104 | /// Number of feature buttons on the HID device. 105 | /// 106 | FNumberFeatureButtons: WORD; 107 | /// 108 | /// Number of input axes on the HID device. 109 | /// 110 | FNumberInputAxes: WORD; 111 | /// 112 | /// Number of output axes on the HID device. 113 | /// 114 | FNumberOutputAxes: WORD; 115 | /// 116 | /// Number of feature axes on the HID device. 117 | /// 118 | FNumberFeatureAxes: WORD; 119 | /// 120 | /// Interface number of the HID device. 121 | /// 122 | FInterfaceNumber: Integer; 123 | private 124 | /// 125 | /// HID Device Handle. 126 | /// 127 | FDeviceHandle: THandle; 128 | public 129 | /// 130 | /// Opens the HID device. 131 | /// 132 | function Open: Boolean; 133 | /// 134 | /// Closes the HID device. 135 | /// 136 | procedure Close; 137 | /// 138 | /// Writes data to the HID device. 139 | /// 140 | function Write(const Macro: THIDMacro): Boolean; 141 | public 142 | /// 143 | /// Path to the HID device. 144 | /// 145 | property DevicePath: string read FDevicePath write FDevicePath; 146 | /// 147 | /// Description of the HID device. 148 | /// 149 | property Description: string read FDescription write FDescription; 150 | /// 151 | /// Manufacturer of the HID device. 152 | /// 153 | property Manufacturer: string read FManufacturer write FManufacturer; 154 | /// 155 | /// Friendly name of the HID device. 156 | /// 157 | property FriendlyName: string read FFriendlyName write FFriendlyName; 158 | /// 159 | /// Location information of the HID device. 160 | /// 161 | property Location: string read FLocation write FLocation; 162 | /// 163 | /// Hardware ID of the HID device. 164 | /// 165 | property HardwareID: string read FHardwareID write FHardwareID; 166 | /// 167 | /// Compatible IDs of the HID device. 168 | /// 169 | property CompatibleIDs: string read FCompatibleIDs write FCompatibleIDs; 170 | /// 171 | /// Service associated with the HID device. 172 | /// 173 | property Service: string read FService write FService; 174 | /// 175 | /// Device class of the HID device. 176 | /// 177 | property DeviceClass: string read FDeviceClass write FDeviceClass; 178 | /// 179 | /// Class GUID of the HID device. 180 | /// 181 | property ClassGUID: string read FClassGUID write FClassGUID; 182 | /// 183 | /// Driver associated with the HID device. 184 | /// 185 | property Driver: string read FDriver write FDriver; 186 | /// 187 | /// Physical device object name of the HID device. 188 | /// 189 | property PhysicalDeviceObjectName: string read FPhysicalDeviceObjectName write FPhysicalDeviceObjectName; 190 | /// 191 | /// Capabilities of the HID device. 192 | /// 193 | property Capabilities: string read FCapabilities write FCapabilities; 194 | /// 195 | /// UI number of the HID device. 196 | /// 197 | property UIDNumber: string read FUIDNumber write FUIDNumber; 198 | /// 199 | /// Manufacturer string from the HID device. 200 | /// 201 | property ManufacturerString: string read FManufacturerString write FManufacturerString; 202 | /// 203 | /// Product string from the HID device. 204 | /// 205 | property ProductString: string read FProductString write FProductString; 206 | /// 207 | /// Serial number string from the HID device. 208 | /// 209 | property SerialNumberString: string read FSerialNumberString write FSerialNumberString; 210 | /// 211 | /// Number of input buttons on the HID device. 212 | /// 213 | property NumberInputButtons: WORD read FNumberInputButtons write FNumberInputButtons; 214 | /// 215 | /// Number of output buttons on the HID device. 216 | /// 217 | property NumberOutputButtons: WORD read FNumberOutputButtons write FNumberOutputButtons; 218 | /// 219 | /// Number of feature buttons on the HID device. 220 | /// 221 | property NumberFeatureButtons: WORD read FNumberFeatureButtons write FNumberFeatureButtons; 222 | /// 223 | /// Number of input axes on the HID device. 224 | /// 225 | property NumberInputAxes: WORD read FNumberInputAxes write FNumberInputAxes; 226 | /// 227 | /// Number of output axes on the HID device. 228 | /// 229 | property NumberOutputAxes: WORD read FNumberOutputAxes write FNumberOutputAxes; 230 | /// 231 | /// Number of feature axes on the HID device. 232 | /// 233 | property NumberFeatureAxes: WORD read FNumberFeatureAxes write FNumberFeatureAxes; 234 | /// 235 | /// Interface number of the HID device. 236 | /// 237 | property InterfaceNumber: Integer read FInterfaceNumber write FInterfaceNumber; 238 | end; 239 | 240 | type 241 | /// 242 | /// HID Device List Class. 243 | /// 244 | THIDDeviceList = class 245 | private 246 | /// 247 | /// Handle for a hidden window. We need this to be able to 248 | /// listen to usb device insert/removal events. 249 | /// 250 | FUSBHandle: HWND; 251 | /// 252 | /// Event when new USB devices is added 253 | /// 254 | FOnUSBArrival: TNotifyEvent; 255 | /// 256 | /// Event when USB device is removed 257 | /// 258 | FOnUSBRemoval: TNotifyEvent; 259 | /// 260 | /// Event for both arrival/removal 261 | /// 262 | FOnUSBChanged: TNotifyEvent; 263 | 264 | /// 265 | /// Hidden window message handler 266 | /// 267 | procedure USBWndProc(var Msg: TMessage); 268 | private 269 | /// 270 | /// List containing the HID devices. 271 | /// 272 | FDevices: TList; 273 | /// 274 | /// Retrieves a device property as a string. 275 | /// 276 | /// Handle to the device information set. 277 | /// Pointer to the SP_DEVINFO_DATA structure. 278 | /// Property identifier. 279 | /// Property value as a string. 280 | function GetDevicePropertyW(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSP_DEVINFO_DATA; Property_: DWORD): string; 281 | /// 282 | /// Retrieves a HID string. 283 | /// 284 | /// Handle to the HID device. 285 | /// Type of HID string to retrieve. 286 | /// HID string. 287 | function GetHIDString(DeviceHandle: THandle; HIDStringType: DWORD): string; 288 | /// 289 | /// Retrieves the capabilities of a HID device. 290 | /// 291 | /// Handle to the HID device. 292 | /// Output HIDP_CAPS structure. 293 | /// True if successful, otherwise False. 294 | function GetHIDCapabilities(DeviceHandle: THandle; out Caps: HIDP_CAPS): Boolean; 295 | /// 296 | /// Adds a HID device to the list. 297 | /// 298 | /// Handle to the device information set. 299 | /// SP_DEVICE_INTERFACE_DATA structure. 300 | procedure AddDevice(DeviceInfoSet: HDEVINFO; DeviceInterfaceData: SP_DEVICE_INTERFACE_DATA); 301 | 302 | /// 303 | /// Get a HID device from the list. 304 | /// 305 | /// Index of the device in the list. 306 | function GetDevice(Index: Integer): THIDDevice; 307 | public 308 | /// 309 | /// Constructor 310 | /// 311 | constructor Create; virtual; 312 | /// 313 | /// Destructor 314 | /// 315 | destructor Destroy; override; 316 | 317 | /// 318 | /// Refresh the list of HID devices. 319 | /// 320 | function Refresh: Boolean; 321 | /// 322 | /// Clear the list of HID devices. 323 | /// 324 | procedure Clear; 325 | /// 326 | /// Get the number of devices in the list 327 | /// 328 | function Count: Integer; 329 | /// 330 | /// Find device based on product string and interface number 331 | /// 332 | function FindDevice(const ProductString: string; const InterfaceNumber: Integer): THIDDevice; 333 | 334 | /// 335 | /// Panels 336 | /// 337 | property Devices[Index: Integer]: THIDDevice read GetDevice; default; 338 | 339 | /// 340 | /// Event when new USB devices is added 341 | /// 342 | property OnUSBArrival: TNotifyEvent read FOnUSBArrival write FOnUSBArrival; 343 | /// 344 | /// Event when USB device is removed 345 | /// 346 | property OnUSBRemoval: TNotifyEvent read FOnUSBRemoval write FOnUSBRemoval; 347 | /// 348 | /// Event for both arrival/removal 349 | /// 350 | property OnUSBChanged: TNotifyEvent read FOnUSBChanged write FOnUSBChanged; 351 | end; 352 | 353 | // SetupAPI.dll 354 | function SetupDiGetClassDevsW(ClassGuid: PGUID; Enumerator: PWideChar; hwndParent: HWND; Flags: DWORD): HDEVINFO; stdcall; external SETUPAPI_DLL name 'SetupDiGetClassDevsW'; 355 | function SetupDiEnumDeviceInterfaces(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSP_DEVINFO_DATA; InterfaceClassGuid: PGUID; MemberIndex: DWORD; DeviceInterfaceData: PSP_DEVICE_INTERFACE_DATA): BOOL; stdcall; external SETUPAPI_DLL name 'SetupDiEnumDeviceInterfaces'; 356 | function SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet: HDEVINFO; DeviceInterfaceData: PSP_DEVICE_INTERFACE_DATA; DeviceInterfaceDetailData: PSP_DEVICE_INTERFACE_DETAIL_DATA_W; DeviceInterfaceDetailDataSize: DWORD; RequiredSize: PDWORD; DeviceInfoData: PSP_DEVINFO_DATA): BOOL; stdcall; external SETUPAPI_DLL name 'SetupDiGetDeviceInterfaceDetailW'; 357 | function SetupDiDestroyDeviceInfoList(DeviceInfoSet: HDEVINFO): BOOL; stdcall; external SETUPAPI_DLL name 'SetupDiDestroyDeviceInfoList'; 358 | function SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSP_DEVINFO_DATA; Property_: DWORD; PropertyRegDataType: PDWORD; PropertyBuffer: PBYTE; PropertyBufferSize: DWORD; RequiredSize: PDWORD): BOOL; stdcall; external SETUPAPI_DLL name 'SetupDiGetDeviceRegistryPropertyW'; 359 | 360 | // hid.dll 361 | function HidD_GetManufacturerString(HidDeviceObject: THandle; Buffer: PWideChar; BufferLength: ULONG): BOOL; stdcall; external HID_DLL name 'HidD_GetManufacturerString'; 362 | function HidD_GetProductString(HidDeviceObject: THandle; Buffer: PWideChar; BufferLength: ULONG): BOOL; stdcall; external HID_DLL name 'HidD_GetProductString'; 363 | function HidD_GetSerialNumberString(HidDeviceObject: THandle; Buffer: PWideChar; BufferLength: ULONG): BOOL; stdcall; external HID_DLL name 'HidD_GetSerialNumberString'; 364 | function HidD_GetPreparsedData(HidDeviceObject: THandle; var PreparsedData: PHIDP_PREPARSED_DATA): BOOL; stdcall; external HID_DLL name 'HidD_GetPreparsedData'; 365 | function HidD_FreePreparsedData(PreparsedData: PHIDP_PREPARSED_DATA): BOOL; stdcall; external HID_DLL name 'HidD_FreePreparsedData'; 366 | function HidP_GetCaps(PreparsedData: PHIDP_PREPARSED_DATA; var Capabilities: HIDP_CAPS): NTSTATUS; stdcall; external HID_DLL name 'HidP_GetCaps'; 367 | 368 | implementation 369 | 370 | //------------------------------------------------------------------------------ 371 | // OPEN HID DEVICE 372 | //------------------------------------------------------------------------------ 373 | function THIDDevice.Open: Boolean; 374 | begin 375 | // Open a handle to the HID device 376 | FDeviceHandle := CreateFile(PChar(FDevicePath), GENERIC_WRITE, FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); 377 | // Check if the handle is valid 378 | Result := FDeviceHandle <> INVALID_HANDLE_VALUE; 379 | end; 380 | 381 | //------------------------------------------------------------------------------ 382 | // CLOSE HID DEVICE 383 | //------------------------------------------------------------------------------ 384 | procedure THIDDevice.Close; 385 | begin 386 | // Close the handle to the HID device 387 | CloseHandle(FDeviceHandle); 388 | end; 389 | 390 | //------------------------------------------------------------------------------ 391 | // WRITE DATA TO HID DEVICE 392 | //------------------------------------------------------------------------------ 393 | function THIDDevice.Write(const Macro: THIDMacro): Boolean; 394 | var 395 | BytesWritten: Cardinal; 396 | begin 397 | // Write data to the HID device 398 | Result := WriteFile(FDeviceHandle, Macro[0], Length(Macro), BytesWritten, nil); 399 | end; 400 | 401 | //------------------------------------------------------------------------------ 402 | // CONSTRUCTOR 403 | //------------------------------------------------------------------------------ 404 | constructor THIDDeviceList.Create; 405 | var 406 | DBI: DEV_BROADCAST_DEVICEINTERFACE; 407 | Size: Integer; 408 | begin 409 | // Call inherited constructor 410 | inherited Create; 411 | // Create the list for storing the HID devices 412 | FDevices := TList.Create; 413 | // Create the hidden window for USB arrival/removal 414 | FUSBHandle := AllocateHWnd(USBWndProc); 415 | // Register the USB listener 416 | Size := SizeOf(DEV_BROADCAST_DEVICEINTERFACE); 417 | ZeroMemory(@DBI, Size); 418 | DBI.dbcc_size := Size; 419 | DBI.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE; 420 | DBI.dbcc_reserved := 0; 421 | DBI.dbcc_classguid := GUID_DEVINTERFACE_USB_DEVICE; 422 | DBI.dbcc_name := 0; 423 | RegisterDeviceNotification(FUSBHandle, @DBI, DEVICE_NOTIFY_WINDOW_HANDLE); 424 | end; 425 | 426 | //------------------------------------------------------------------------------ 427 | // DESTRUCTOR 428 | //------------------------------------------------------------------------------ 429 | destructor THIDDeviceList.Destroy; 430 | var 431 | I: Integer; 432 | begin 433 | // Free all the HID devices 434 | for I := 0 to FDevices.Count - 1 do THIDDevice(FDevices[I]).Free; 435 | // Free the HID device list 436 | FDevices.Free; 437 | // Call inherited destructor 438 | inherited; 439 | end; 440 | 441 | //------------------------------------------------------------------------------ 442 | // USB WND PROC 443 | //------------------------------------------------------------------------------ 444 | procedure THIDDeviceList.USBWndProc(var Msg: TMessage); 445 | var 446 | DeviceType: Integer; 447 | Data: PDEV_BROADCAST_HDR; 448 | begin 449 | // Only handle device change messages 450 | if (Msg.Msg = WM_DEVICECHANGE) then 451 | begin 452 | try 453 | // Handle device arrival and removal 454 | if (Msg.wParam = DBT_DEVICEARRIVAL) or (Msg.wParam = DBT_DEVICEREMOVECOMPLETE) then 455 | begin 456 | // Get Data 457 | Data := PDEV_BROADCAST_HDR(Msg.lParam); 458 | // Get device type 459 | DeviceType := Data^.dbch_devicetype; 460 | // Make sure we are handling events for the right device type (USB) only 461 | if (DeviceType = DBT_DEVTYP_DEVICEINTERFACE) then 462 | begin 463 | // Arrival 464 | if Msg.wParam = DBT_DEVICEARRIVAL then 465 | begin 466 | if Assigned(OnUSBArrival) then OnUSBArrival(Self); 467 | end else 468 | // Removal 469 | begin 470 | if Assigned(OnUSBRemoval) then OnUSBRemoval(Self); 471 | end; 472 | // Both 473 | if Assigned(OnUSBChanged) then OnUSBChanged(Self); 474 | end; 475 | end; 476 | except 477 | // When an error occurs, let the application handle the exception 478 | Application.HandleException(Self); 479 | end; 480 | end else 481 | // Let Windows handle other messages. 482 | Msg.Result := DefWindowProc(FUSBHandle, Msg.Msg, Msg.wParam, Msg.lParam); 483 | end; 484 | 485 | //------------------------------------------------------------------------------ 486 | // GET HID DEVICE PROPERTY 487 | //------------------------------------------------------------------------------ 488 | function THIDDeviceList.GetDevicePropertyW(DeviceInfoSet: Pointer; DeviceInfoData: PSP_DEVINFO_DATA; Property_: Cardinal): string; 489 | var 490 | Buffer: array[0..1023] of WideChar; 491 | RequiredSize: DWORD; 492 | RegDataType: DWORD; 493 | begin 494 | // Initialize result 495 | Result := ''; 496 | // Try to get the Device Registry Property 497 | if SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, Property_, @RegDataType, @Buffer[0], SizeOf(Buffer), @RequiredSize) then 498 | begin 499 | // Set the result 500 | Result := Buffer; 501 | end; 502 | end; 503 | 504 | //------------------------------------------------------------------------------ 505 | // GET HID DEVICE STRING 506 | //------------------------------------------------------------------------------ 507 | function THIDDeviceList.GetHIDString(DeviceHandle: NativeUInt; HIDStringType: Cardinal): string; 508 | var 509 | Buffer: array[0..255] of WideChar; 510 | begin 511 | // Initialize result 512 | Result := ''; 513 | 514 | case HIDStringType of 515 | // HID Manufacturer string. 516 | HID_MANUFACTURER_STRING: 517 | if HidD_GetManufacturerString(DeviceHandle, Buffer, SizeOf(Buffer)) then Result := Buffer; 518 | 519 | // HID Product string. 520 | HID_PRODUCT_STRING: 521 | if HidD_GetProductString(DeviceHandle, Buffer, SizeOf(Buffer)) then Result := Buffer; 522 | 523 | // HID Serial Code string. 524 | HID_SERIALCODE_STRING: 525 | if HidD_GetSerialNumberString(DeviceHandle, Buffer, SizeOf(Buffer)) then Result := Buffer; 526 | end; 527 | end; 528 | 529 | //------------------------------------------------------------------------------ 530 | // GET HID DEVICE CAPABILITIES 531 | //------------------------------------------------------------------------------ 532 | function THIDDeviceList.GetHIDCapabilities(DeviceHandle: NativeUInt; out Caps: HIDP_CAPS): Boolean; 533 | var 534 | PreparsedData: PHIDP_PREPARSED_DATA; 535 | begin 536 | // Initialize result 537 | Result := False; 538 | // Try to get the Preparsed data 539 | if HidD_GetPreparsedData(DeviceHandle, PreparsedData) then 540 | try 541 | // Set result 542 | Result := HidP_GetCaps(PreparsedData, Caps) = HIDP_STATUS_SUCCESS; 543 | finally 544 | // Free the preparsed data 545 | HidD_FreePreparsedData(PreparsedData); 546 | end; 547 | end; 548 | 549 | //------------------------------------------------------------------------------ 550 | // ADD HID DEVICE 551 | //------------------------------------------------------------------------------ 552 | procedure THIDDeviceList.AddDevice(DeviceInfoSet: HDEVINFO; DeviceInterfaceData: SP_DEVICE_INTERFACE_DATA); 553 | 554 | function GetInterfaceNumber(DevicePath: string): DWORD; 555 | var 556 | InterfaceNumberStr: string; 557 | VidPos, MiPos: Integer; 558 | begin 559 | // Initialize result 560 | Result := 0; 561 | // Find VID pos 562 | VidPos := Pos('#vid_', LowerCase(DevicePath)); 563 | 564 | // If we found the VID pos 565 | if VidPos > 0 then 566 | begin 567 | // Find MI pos 568 | MiPos := Pos('mi_', LowerCase(Copy(DevicePath, VidPos + 1, Length(DevicePath) - VidPos))); 569 | // If we found MI pos 570 | if MiPos > 0 then 571 | begin 572 | // Adjust offset 573 | Inc(MiPos, VidPos); 574 | // Extract the interface number 575 | InterfaceNumberStr := Copy(DevicePath, MiPos + 3, 2); 576 | Result := StrToIntDef(InterfaceNumberStr, 1); 577 | end; 578 | end; 579 | end; 580 | 581 | var 582 | DeviceInterfaceDetailData: PSP_DEVICE_INTERFACE_DETAIL_DATA_W; 583 | DeviceInfoData: SP_DEVINFO_DATA; 584 | RequiredSize, DetailDataSize: DWORD; 585 | DeviceHandle: THandle; 586 | HIDDevice: THIDDevice; 587 | HIDCaps: HIDP_CAPS; 588 | begin 589 | // Retrieve the required size for the device interface detail data structure. 590 | SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, @DeviceInterfaceData, nil, 0, @RequiredSize, nil); 591 | DetailDataSize := RequiredSize; 592 | 593 | // Allocate memory for the device interface detail data structure. 594 | GetMem(DeviceInterfaceDetailData, DetailDataSize); 595 | try 596 | // Set the size of the device interface detail data structure. 597 | DeviceInterfaceDetailData.cbSize := SizeOf(DWORD) + SizeOf(WideChar); 598 | 599 | // Initialize the device information data structure to zero. 600 | ZeroMemory(@DeviceInfoData, SizeOf(DeviceInfoData)); 601 | DeviceInfoData.cbSize := SizeOf(DeviceInfoData); 602 | 603 | // Retrieve the device interface detail. 604 | if SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, @DeviceInterfaceData, DeviceInterfaceDetailData, DetailDataSize, @RequiredSize, @DeviceInfoData) then 605 | begin 606 | // Create a new HID device instance. 607 | HIDDevice := THIDDevice.Create; 608 | 609 | // Store the device path in the HID device instance. 610 | HIDDevice.DevicePath := WideCharToString(@DeviceInterfaceDetailData.DevicePath); 611 | 612 | // Open a handle to the HID device. 613 | DeviceHandle := CreateFile(PChar(HIDDevice.DevicePath), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); 614 | if DeviceHandle <> INVALID_HANDLE_VALUE then 615 | begin 616 | // Retrieve and store the manufacturer string. 617 | HIDDevice.ManufacturerString := GetHIDString(DeviceHandle, 1); 618 | // Retrieve and store the product string. 619 | HIDDevice.ProductString := GetHIDString(DeviceHandle, 2); 620 | // Retrieve and store the serial number string. 621 | HIDDevice.SerialNumberString := GetHIDString(DeviceHandle, 3); 622 | 623 | // Retrieve and store the HID capabilities. 624 | if GetHIDCapabilities(DeviceHandle, HIDCaps) then 625 | begin 626 | HIDDevice.NumberInputButtons := HIDCaps.NumberInputButtonCaps; 627 | HIDDevice.NumberOutputButtons := HIDCaps.NumberOutputButtonCaps; 628 | HIDDevice.NumberFeatureButtons := HIDCaps.NumberFeatureButtonCaps; 629 | HIDDevice.NumberInputAxes := HIDCaps.NumberInputValueCaps; 630 | HIDDevice.NumberOutputAxes := HIDCaps.NumberOutputValueCaps; 631 | HIDDevice.NumberFeatureAxes := HIDCaps.NumberFeatureValueCaps; 632 | end; 633 | 634 | // Close the handle to the HID device. 635 | CloseHandle(DeviceHandle); 636 | end; 637 | 638 | // Retrieve and store the device description. 639 | HIDDevice.Description := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_DEVICEDESC); 640 | // Retrieve and store the manufacturer name. 641 | HIDDevice.Manufacturer := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_MFG); 642 | // Retrieve and store the friendly name. 643 | HIDDevice.FriendlyName := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_FRIENDLYNAME); 644 | // Retrieve and store the location information. 645 | HIDDevice.Location := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_LOCATION_INFORMATION); 646 | // Retrieve and store the hardware ID. 647 | HIDDevice.HardwareID := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_HARDWAREID); 648 | // Retrieve and store the compatible IDs. 649 | HIDDevice.CompatibleIDs := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_COMPATIBLEIDS); 650 | // Retrieve and store the service name. 651 | HIDDevice.Service := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_SERVICE); 652 | // Retrieve and store the device class. 653 | HIDDevice.DeviceClass := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_CLASS); 654 | // Retrieve and store the class GUID. 655 | HIDDevice.ClassGUID := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_CLASSGUID); 656 | // Retrieve and store the driver key. 657 | HIDDevice.Driver := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_DRIVER); 658 | // Retrieve and store the physical device object name. 659 | HIDDevice.PhysicalDeviceObjectName := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME); 660 | // Retrieve and store the capabilities. 661 | HIDDevice.Capabilities := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_CAPABILITIES); 662 | // Retrieve and store the UI number. 663 | HIDDevice.UIDNumber := GetDevicePropertyW(DeviceInfoSet, @DeviceInfoData, SPDRP_UINUMBER); 664 | // Extract the interface number from the device path 665 | HIDDevice.InterfaceNumber := GetInterfaceNumber(HIDDevice.DevicePath); 666 | 667 | // Add the HID device to the device list. 668 | FDevices.Add(HIDDevice); 669 | end; 670 | finally 671 | // Free the allocated memory for the device interface detail data structure. 672 | FreeMem(DeviceInterfaceDetailData); 673 | end; 674 | end; 675 | 676 | //------------------------------------------------------------------------------ 677 | // GET HID DEVICE BY INDEX 678 | //------------------------------------------------------------------------------ 679 | function THIDDeviceList.GetDevice(Index: Integer): THIDDevice; 680 | begin 681 | // Initialize result 682 | Result := nil; 683 | // Exit here if the index is out of range 684 | if (Index < 0) or (Index > FDevices.Count -1) then Exit; 685 | // Return the device from the list of devices 686 | Result := FDevices.Items[Index]; 687 | end; 688 | 689 | //------------------------------------------------------------------------------ 690 | // REFRESH HID DEVICE LIST 691 | //------------------------------------------------------------------------------ 692 | function THIDDeviceList.Refresh: Boolean; 693 | var 694 | DeviceInfoSet: HDEVINFO; 695 | DeviceInterfaceData: SP_DEVICE_INTERFACE_DATA; 696 | MemberIndex: DWORD; 697 | begin 698 | // Initialize result 699 | Result := False; 700 | 701 | // Clear the list with HID devices 702 | FDevices.Clear; 703 | 704 | // Retrieve a handle to a device information set that contains devices that 705 | // are currently present and expose the specified device interface. 706 | DeviceInfoSet := SetupDiGetClassDevsW(@GUID_DEVINTERFACE_HID, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE); 707 | 708 | // Check if the handle to the device information set is valid. 709 | if DeviceInfoSet = nil then Exit; 710 | 711 | try 712 | MemberIndex := 0; 713 | while True do 714 | begin 715 | // Initialize the device interface data structure to zero. 716 | ZeroMemory(@DeviceInterfaceData, SizeOf(DeviceInterfaceData)); 717 | DeviceInterfaceData.cbSize := SizeOf(DeviceInterfaceData); 718 | 719 | // Enumerate the device interfaces in the device information set. 720 | if not SetupDiEnumDeviceInterfaces(DeviceInfoSet, nil, @GUID_DEVINTERFACE_HID, MemberIndex, @DeviceInterfaceData) then Break; 721 | 722 | // Add the enumerated device to the list. 723 | AddDevice(DeviceInfoSet, DeviceInterfaceData); 724 | Inc(MemberIndex); 725 | end; 726 | finally 727 | // Destroy the device information set and release associated resources. 728 | SetupDiDestroyDeviceInfoList(DeviceInfoSet); 729 | end; 730 | 731 | // Set the result to true, indicating success 732 | Result := True; 733 | end; 734 | 735 | //------------------------------------------------------------------------------ 736 | // CLEAR DEVICES IN THE LIST 737 | //------------------------------------------------------------------------------ 738 | procedure THIDDeviceList.Clear; 739 | begin 740 | FDevices.Clear; 741 | end; 742 | 743 | //------------------------------------------------------------------------------ 744 | // GET THE NUMBER OF DEVICES IN THE LIST 745 | //------------------------------------------------------------------------------ 746 | function THIDDeviceList.Count: Integer; 747 | begin 748 | Result := FDevices.Count; 749 | end; 750 | 751 | //------------------------------------------------------------------------------ 752 | // FIND DEVICE BY PRODUCT STRING AND INTERFACE NUMBER 753 | //------------------------------------------------------------------------------ 754 | function THIDDeviceList.FindDevice(const ProductString: string; const InterfaceNumber: Integer): THIDDevice; 755 | var 756 | I: Integer; 757 | begin 758 | // Set initial result 759 | Result := nil; 760 | // First refresh the list 761 | if not Refresh then Exit; 762 | // Try to find the device 763 | for I := 0 to Count - 1 do 764 | if (CompareText(Devices[I].ProductString, ProductString) = 0) and (Devices[I].InterfaceNumber = InterfaceNumber) then 765 | begin 766 | // Set result to found device 767 | Result := Devices[I]; 768 | // Break the loop 769 | Break; 770 | end; 771 | end; 772 | 773 | end. 774 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------