AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

Post your working scripts, libraries and tools for AHK v1.1 and older
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

17 Apr 2018, 15:36

if i just run the script normally i get no error but if i open it in scrite turn on debug mode and hit f11
i recorded it and upped it to sendspace https://www.sendspace.com/file/so2qew
Last edited by OCP on 20 Jun 2018, 03:16, edited 1 time in total.
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

17 Apr 2018, 15:47

no hurries i am off to bed soon i hope i am not the only one giving you headache stuff :) take it easy i have no hurry
Last edited by OCP on 20 Jun 2018, 03:16, edited 1 time in total.
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

18 Apr 2018, 07:59

i was wondering could it be you are on win10 (i dunno if you are) and me on win7 and that the code needs to be different depending on system?
Last edited by OCP on 20 Jun 2018, 03:16, edited 1 time in total.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

18 Apr 2018, 10:30

Yes, I am on Win10.
The underlying code that does most of the heavy lifting is .NET, so I guess it could depend on what version of .NET you have installed.
AHI currently targets .NET 4.6.1, although I could probably drop that lower.
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

18 Apr 2018, 11:57

i see i have v4.7.1

if i delete or remove the interception.dll i do get that error saying autohotinterception.dll failed to load so i think that the interception.dll is working just not the other dll

what if i install 4.6.1
Last edited by OCP on 20 Jun 2018, 03:16, edited 1 time in total.
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

22 Apr 2018, 14:52

hi thnx for the update

i no longer get a bug report in or outside scrite so it should be working but i still get a empty monitor gui you have any ideas what i could do?
Last edited by OCP on 20 Jun 2018, 03:15, edited 1 time in total.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

23 Apr 2018, 13:36

@OCP, I think I may have worked out what it is.

https://support.microsoft.com/en-gb/hel ... in-windows

It seems that if you build .NET code on VS2015 or later (Which I do), then this targets the "Universal C runtime", which your old WinXP system will probably not have.
You can get an update for Win7 and later, but not XP.
I will ask around and see if I can maybe build a copy that works with normal .NET or something, even if just to satisfy my curiosity as to why it doesn't work for you
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

23 Apr 2018, 14:02

Now that with AHI we can make a 2nd keyboard totally independent of the 1st keyboard, my mind got to thinking of how you could take advantage of that.
I got to thinking about hotstrings - normally hotstrings have certain limitations because you are trying to preserve the normal functionality of the key, whilst also allowing hotstrings. But with AHI, we no longer care about the normal functionality of a key that is used as part of a hotstring, meaning AHK's built-in hotstring system is hamstrung by limitations that are no longer relevant.
So I started to write my own.

The ultimate goal is to provide on-screen cues to help guide the user through the hotstrings. ie if you have two hotstrings "aaa", "abc" and "aac", then typing "a" should show all three, if you type "aa" it should show "aaa" and "aac" etc, along with showing a hint as to what these hotstrings actually do.

In addition to hotstrings, my thought was to write my own equivalent of AHK's Input command for AHI, so you could do keyboard-specific input of arbitrary text.
For example, let's say you are playing a flight simulator and want to control the angle of the flaps.
You would have a hotstring FL, which would then trigger an input, which would wait for you to type a number and hit ENTER.
So to set flaps to 50% would be FL50<ENTER>

This would also transfer to other use-cases, eg in an art package you could have OP75<ENTER> to set Opacity to 75%, or even maybe <F1>ARIAL<ENTER> to set font to Arial

I managed to get *something* working, but I kind of lack inspiration to progress, so I am posting it here in the hopes that maybe some of the community can come up with some ideas as to how it should work.

This demo has two hotstrings
VO<NUMBERS><ENTER> = Set Volume
BR<TEXT><ENTER> = Launch browser, <TEXT> is looked up in the browserChoices array.

Code: Select all

#SingleInstance force
#Persistent
#include Lib\AutoHotInterception.ahk

OutputDebug DBGVIEWCLEAR

global AHI := new AutoHotInterception()
global keyboardId := AHI.GetKeyboardId(0x413C, 0x2107, 1)

sm := new SequenceManager(Func("CommandBufferChange"))
sm.Add("Browse", ["b", "r"], "Launches a Browser to common locations", Func("BrowserStart"))
sm.Add("Volume", ["v", "o"], "Sets the volume to the specified level", Func("VolumeStart"))

global browserChoices := {goo: "http://google.com"}
return

^Esc::
	Exit()
	return

RenderSeqence(seq){
	global sm
	if (!seq.Length()){
		return ""
	}
	for i, key in seq {
		out .= sm.KeyNames[key] "  "
	}
	return out
}

ToolTip(text := "", dur := -1){
	ToolTip % text
	if (dur != -1)
		SetTimer, TooltipEnd, % - dur
}

TooltipEnd:
	ToolTip
	return

CommandBufferChange(buf, partialMatches){
	global sm
	commandStr := RenderSeqence(buf)
	if (commandStr == ""){
		ToolTip()
		return
	}
	
	for name, command in partialMatches {
		partialMatchStr .= name ": [ " RenderSeqence(command.Seq) " ] : " command.Description "`n"
	}
	
	ToolTip("Partial Matches:`n " partialMatchStr " `nBuffer:`n" commandStr)
}

BrowserStart(){
	global sm
	sm.EnableInputMode(Func("BrowserEnd"), Func("BroswerBufferChange"))
	ToolTip("Browser Choice:`n ")
}

BroswerBufferChange(buf){
	global sm, browserChoices
	text := sm.GetTextInput()
	for name, url in browserChoices {
		if (InStr(name, text, flase, 1)){
			partialMatches .= name ": " url "`n"
		}
	}
	ToolTip("Partial Matches:`n" partialMatches "`n`nBuffer:`n" RenderSeqence(buf))
}

BrowserEnd(){
	global sm, browserChoices
	text := sm.GetTextInput()
	if (browserChoices.HasKey(text)){
		run % browserChoices[text]
	}
	ToolTip()
}

VolumeStart(){
	global sm
	sm.EnableInputMode(Func("VolumeEnd"), Func("VolumeBufferChange"))
	ToolTip("Input Volume: `n ")
}

VolumeEnd(){
	global sm
	text := sm.GetNumberInput()
	ToolTip("Set Volume: " text, 500)
	SoundSet, % text
}

VolumeBufferChange(buf){
	global sm
	if (!buf.Length()){
		ToolTip()
		return
	}
	for i, key in buf {
		out .= sm.KeyNames[key] "  "
	}
	ToolTip("Input Volume:`n" out)
}

Exit(){
	SoundBeep, 500, 250
	ExitApp
}

; ===============================================================

class SequenceManager {
	sequenceArray := {}					; A store of all the sequences
	sequencesByLength := {}				; A lookup table of sequences, by length of sequence
	longestSequence := 0				; Length of the longest Sequence
	shortestSequence := -1				; Length of the shortest Sequence
	textInputMode := 0					; 0 = Normal (Command) mode, 1 = Input mode
	commandBuffer := []					; When in Command Mode, the characters in the command buffer
	commandBufferUpdateCallback := 0	; The callback for changes in command mode
	commandBufferPartialMatches := {}	; When building up a command, the commands which partially match
	
	KeyNames := {}
	
	NumberCodes := {11: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7, 9: 8, 10: 9, 12: "-", 13: "+"	; Number strip
		, 82: 0, 79: 1, 80: 2, 81: 3, 75: 4, 76: 5, 77: 6, 71: 7, 72: 8, 73: 9, 74: "-", 78: "+"}	; Numpad

	LetterCodes := {16:1, 17:1, 18:1, 19:1, 20:1, 21:1, 22:1, 23:1, 24:1, 25:1	; top row
		, 30:1, 31:1, 32:1, 33:1, 34:1, 35:1, 36:1, 37:1, 38:1					; middle row
		,44:1, 45:1, 46:1, 47:1, 48:1, 49:1, 50:1}								; bottom row

	BackSpaceCodes := {14:1}	; Backspace
	
	OkCodes := {28:1, 284:1}	; Enter / NumpadEnter
	
	CancelCodes := {1:1}		; Esc
	
	__New(commandBufferUpdateCallback := 0){
		this.commandBufferUpdateCallback := commandBufferUpdateCallback
		this.CreateHotkeys()
	}
	
	Add(name, _seq, desc, callback){
		seq := []
		for i, n in _seq {
			code := GetKeySC(n)
			if (!code){
				MsgBox % "Unknown key " n
				ExitApp
			}
			seq.Push(code)
		}
		sequence := new this.Sequence(name, seq, desc, callback)
		this.sequenceArray[name] := sequence
		max := seq.Length()
		if (!this.sequencesByLength.HasKey(max)){
			this.sequencesByLength[max] := {}
		}
		this.sequencesByLength[max][name] := sequence
		if (max > this.longestSequence){
			this.longestSequence := max
		} else if (this.shortestSequence == -1 || max < this.shortestSequence){
			this.shortestSequence := max
		}
	}
	
	EnableInputMode(callback, inputBufferUpdateCallback := 0){
		this.SetInputState(1)
		this.InputCallback := callback
		this.inputBufferUpdateCallback := inputBufferUpdateCallback
	}
	
	GetTextInput(){
		out := ""
		for i, code in this.InputBuffer {
			printable := this.LookupLetter(code)
			if (printable == -1){
				return "ERROR"
			}
			out .= printable
		}
		return out
	}
	
	GetNumberInput(){
		minus := 0
		start := 0
		max := this.InputBuffer.Length()
		out := ""
		if (this.NumberCodes[this.InputBuffer[1]] == "-"){
			out := "-"
			start++
		}
		Loop % max - start {
			code := this.InputBuffer[A_Index + start]
			num := this.LookupNumber(code)
			if (num == -1){
				return "ERROR"
			}
			out .= num
		}
		return out
	}
	
	LookupNumber(code){
		if (this.NumberCodes.HasKey(code)){
			return this.NumberCodes[code]
		}
		return -1
	}
	
	LookupLetter(i){
		if (this.LetterCodes.HasKey(i)){
			code := Format("{:x}", i)
			return GetKeyName("SC" code)
		}
		return -1
	}
	
	SetInputState(state){
		this.Log("Setting Input state to " state)
		this.textInputMode := state
		if (state){
			this.InputBuffer := []
		}
	}
	
	InputEvent(code, state){
		if (!state)
			return
		if (this.textInputMode){
			if (code == 0x1c || code == 0x11c){
				;~ this.Log("ENTER")
				this.SetInputState(0)
				fn := this.InputCallback
				SetTimer, % fn, -0
				;~ this.InputCallback.Call(this.InputBuffer)
			} else {
				this.Log("Adding " code " to buffer")
				this.InputBuffer.Push(code)
				if (this.inputBufferUpdateCallback != 0){
					this.inputBufferUpdateCallback.Call(this.InputBuffer)
				}
			}
		} else {
			if (this.BackSpaceCodes.HasKey(code)){
				this.commandBuffer.Pop(code)
			} else if (this.CancelCodes.HasKey(code)){
				this.commandBuffer := []
			} else {
				this.commandBuffer.Push(code)
				name := this.FindMatchingSequence()
				if (name != -1){
					this.commandBuffer := []
					fn := this.sequenceArray[name].Callback
					SetTimer, % fn, -0
				}
			}
			this.CommandBufferUpdate()
		}
		this.Log("IO Event: Code: " code ", Key: " this.KeyNames[code])
	}
	
	FindMatchingSequence(){
		this.commandBufferPartialMatches := {}
		max := this.commandBuffer.Length()
		;~ if (max >= this.shortestSequence){
			Loop % this.longestSequence {
				if (!this.sequencesByLength.HasKey(A_Index))
					continue
				for name, sequence in this.sequencesByLength[A_Index] {
					match := 1
					for i, c in sequence.Seq {
						if (i > max){
							; Partial match
							this.commandBufferPartialMatches[name] := sequence
							match := 0
							break
						}
						if (this.commandBuffer[i] != c){
							match := 0
							break
						}
					}
					if (match)
						return name
				}
			}
			; Same number of characters in buffer as longest sequence, none matched
			; Reset here?
		;~ }
		return -1
	}
	
	CommandBufferUpdate(){
		if (this.commandBufferUpdateCallback != 0)
			this.commandBufferUpdateCallback.Call(this.commandBuffer, this.commandBufferPartialMatches)
	}
	
	Log(text){
		OutputDebug % "AHK| SequenceManager | " text
	}
	
	CreateHotkeys(){
		this.KeyNames := {}
		Loop 512 {
			i := A_Index
			code := Format("{:x}", i)
			name := GetKeyName("SC" code)
			if (name == "")
				continue
			this.KeyNames[i] := name
			fn := this.InputEvent.Bind(this, i)
			AHI.SubscribeKey(keyboardId, i, true, fn)
			;~ fn := this.InputEvent.Bind(this, 1, code, name)
			;~ hotkey, % "SC" code, % fn
		}
	}
	
	class Sequence {
		__New(name, seq, desc, callback){
			this.Name := name
			this.Seq := seq
			this.Description := desc
			this.Max := this.Seq.Length()
			this.Callback := callback
		}
		
		Log(text){
			OutputDebug % "AHK| Sequence " this.name " | " text
		}
	}

}
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

23 Apr 2018, 15:26

hi thnx for the posts, i am on win 7 btw ok now i go read it all :)
Last edited by OCP on 26 Jul 2018, 22:42, edited 1 time in total.
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

24 Apr 2018, 04:21

hello

i tried updating but it says i already had that update for .net, i seam to be the weird case here :) perhaps something is just wrong with my current windows installation something i cant go into at this point i am to invested in my current installation for now anyway.

i am thinking if i should just buy me a programmable keyboard for my 65 hotkeys or wait until i have upgrade money for a new pc and then go this route again

anyway if i can help you in any way testing something over here just let me know i keep an eye on this thread
Last edited by OCP on 20 Jun 2018, 03:15, edited 1 time in total.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

16 Jun 2018, 08:00

I have PS/2 keyboard and monitor.ahk does not recognize it.
Does your wrapper work with PS/2 devices?
And second question:
Why if I use usb keyboard I can not send scancode which consists of 3 digits?
For example sc148 - Up.
crumbl3d
Posts: 3
Joined: 23 Jun 2018, 17:01
Contact:

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

23 Jun 2018, 17:15

Is it possible to get device id (Interception ID) after the script is loaded? I can't even get it to work inside a function, only works globally. I need a way to get device id on WM_DEVICECHANGE message and to subscribe to the mouse wheel event from the specific USB mouse. One idea I had is to reload the script every time but those messages are very common so I think it is a very hacky hack...

EDIT: I'm an idiot... Of course I forgot to declare global AHI object inside the function... I am so used to C/C++ and other languages I completely forgot I needed to redeclare the global variables. :P
Last edited by crumbl3d on 24 Jun 2018, 04:41, edited 1 time in total.
crumbl3d
Posts: 3
Joined: 23 Jun 2018, 17:01
Contact:

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

23 Jun 2018, 17:21

malcev wrote:I have PS/2 keyboard and monitor.ahk does not recognize it.
Does your wrapper work with PS/2 devices?
And second question:
Why if I use usb keyboard I can not send scancode which consists of 3 digits?
For example sc148 - Up.
I'm also wondering if this is doable. I'm using a laptop so the keyboard is being reported as a PS/2 keyboard but apparently no valid VID/PID. It would be great if I could use this to remap the embedded keyboard buttons which are not accessible by AutoHotKey.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

24 Jun 2018, 15:07

crumbl3d, I think, that it is a bug of this version of autohotkey wrapper.
You can use this one:
https://autohotkey.com/boards/viewtopic.php?f=5&t=26306
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

25 Jun 2018, 06:31

@malcev - AFAIK VID/PID is a USB concept, so PS/2 devices will not have one.
IIRC Intercept (The other wrapper you linked) does not filter by specific VID/PID, by default it filters every device.
ie it just uses the default "Predicate", which matches everything.
set_filter(context, "keyboard","FILTER_KEY_ALL|FILTER_KEY_E0|FILTER_KEY_E1")
if is_keyboard(device)
For this reason, it may see PS/2 input, whereas AHI would not. (Because AHI only hooks what you tell it to, and there's currently no way to tell it to hook PS/2 devices)

If this were the case, then altering AHI to allow subscribing to PS/2 devices would probably also be possible.

@crumbl3d - If you wanna have a play around with the AHI source and see if you can see input coming from the PS/2 device - I guess it maybe has no VID/PID, but may still have an ID or something.
I am away on holiday until Fri 29th FYI, and I can probably lay my hands on a PS/2 keyboard for testing on the following Monday.
Last edited by evilC on 25 Jun 2018, 06:59, edited 1 time in total.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

25 Jun 2018, 06:54

@crumbl3d: This is where AHI decides whether to filter a device or not:
https://github.com/evilC/AutoHotInterce ... er.cs#L347

If using a PS/2 keyboard, set it to return true if device < 11, which will filter all keyboards. Just don't set it to always return true, as then all keyboard and mouse input would lock up while you debug ;)
If the PS/2 device is gonna work, you would then see input start coming in from it in PollThread()

As I said, I guess that maybe PS/2 devices have no VID/PID, but do get assigned an ID. So I guess that all we need is an alternative for GetKeyboardId/GetMouseId that works with PS/2 devices
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: AutoHotInterception (AHI): Multi-Keyboard / Multi-Mouse support for AHK. Per-device blocking!

25 Jun 2018, 22:25

PS/2 has ID.
http://download.microsoft.com/download/ ... devids.txt
Why if I use usb keyboard I can not send or remap scancode which consists of 3 digits?
For example sc148 - Up.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 112 guests