Page 6 of 18

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

Posted: 17 Apr 2018, 15:36
by OCP
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

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

Posted: 17 Apr 2018, 15:39
by evilC
Well, as they say, pics or it didn't happen and that just happened.
Can't really address it tonight, but something seems very wrong - a script should not behave differently in and outside scite.

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

Posted: 17 Apr 2018, 15:47
by OCP
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

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

Posted: 18 Apr 2018, 07:59
by OCP
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?

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

Posted: 18 Apr 2018, 10:30
by evilC
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.

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

Posted: 18 Apr 2018, 11:57
by OCP
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

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

Posted: 22 Apr 2018, 13:30
by evilC
@OCP: .NET is backwards compatible, so if you have latest you are good.

@All: AHI v3.4 is released!

https://github.com/evilC/AutoHotInterce ... s/tag/v3.4

Includes a bunch of checks to detect various issues reported by OCP, plus includes some improvements to "Extended key" handling.

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

Posted: 22 Apr 2018, 14:52
by OCP
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?

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

Posted: 23 Apr 2018, 07:30
by evilC
I have some leads - I need to work out what the dependencies are for the various DLLs.
I suspect that you are missing some redistributable (Maybe Visual C runtime?)

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

Posted: 23 Apr 2018, 13:36
by evilC
@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

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

Posted: 23 Apr 2018, 14:02
by evilC
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
		}
	}

}

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

Posted: 23 Apr 2018, 15:26
by OCP
hi thnx for the posts, i am on win 7 btw ok now i go read it all :)

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

Posted: 24 Apr 2018, 04:21
by OCP
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

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

Posted: 16 Jun 2018, 08:00
by malcev
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.

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

Posted: 23 Jun 2018, 17:15
by crumbl3d
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

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

Posted: 23 Jun 2018, 17:21
by crumbl3d
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.

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

Posted: 24 Jun 2018, 15:07
by malcev
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

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

Posted: 25 Jun 2018, 06:31
by evilC
@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.

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

Posted: 25 Jun 2018, 06:54
by evilC
@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

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

Posted: 25 Jun 2018, 22:25
by malcev
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.