Multiple keyboards and selective input blocking

Just starting out? Need help? Post your questions and find answers here.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Multiple keyboards and selective input blocking

Post by Lunasole »

Hi once more ^^

I want to perform very nontrivial input handling:
1) Monitor input from several keyboards at once and differentiate it
2) Block all keyboard input by default (not allowing it pass into a system, reaching any application window)
3) Process the collected input (storing it in separated buffers, one per device) and in some conditions, pass it after a small delay to a system (to a foreground window)

The first and third steps I've made successfully (using Raw Input to collect inputs and SendInput to emulate).

The second is more serious ^^ I've made some working stuff, but it is full of unstable workarounds, also several more such problems exposed recently.

I also tried SetWindowsHookEx() API, which makes possible to block any KB input. But it can't be used because it is incompatible with Raw Input processing (if set up keyboard_LL hook, Raw Input callback will never receive any messages).

Any other ideas how it is possible to block keyboard input for whole system, using user-mode code? :)
Or well, if there is some full of hacks driver for this it will be fine too.


PS. Following code used to capture inputs

Code: Select all

EnableExplicit

;{ Raw Input }

	; GetRawInputData() command flags
	#RID_HEADER			= $10000005 ; get header structure
	#RID_INPUT			= $10000003 ; get whole RAWINPUT structure
	
	; Types of raw input data
	#RIM_TYPEMOUSE    	= 0
	#RIM_TYPEKEYBOARD	= 1
	#RIM_TYPEHID		= 2
	
	; RAWINPUTDEVICE flags
	#RIDEV_REMOVE 		= $00000001 
	#RIDEV_EXCLUDE		= $00000010
	#RIDEV_PAGEONLY 	= $00000020
	#RIDEV_NOLEGACY		= $00000030
	#RIDEV_INPUTSINK  	= $00000100
	#RIDEV_CAPTUREMOUSE	= $00000200
	#RIDEV_NOHOTKEYS	= $00000200
	#RIDEV_APPKEYS		= $00000400
	#RIDEV_EXINPUTSINK	= $00001000
	#RIDEV_DEVNOTIFY	= $00002000
	

	; GetRawInputDeviceInfo() flags
	#RIDI_PREPARSEDDATA = $20000005 ; returns previously parsed data 
	#RIDI_DEVICENAME 	= $20000007	;- a string containing the device name
	#RIDI_DEVICEINFO 	= $2000000b	;- an RIDI_DEVICE_INFO Structure
	
	
	Structure RAWINPUTDEVICE
		usUsagePage.w
		usUsage.w
		dwFlags.l
		hwndTarget.l
	EndStructure
	
	Structure RAWINPUTDEVICELIST
		hDevice.l
		dwType.l			; The type of device. RIM_TYPEKEYBOARD, RIM_TYPEMOUSE, RIM_TYPEHID
	EndStructure
	
	
	Structure RAWINPUTHEADER
		dwType.l			; The type of raw input. RIM_TYPEKEYBOARD, RIM_TYPEMOUSE, RIM_TYPEHID
		dwSize.l
		hDevice.l			; A handle to the device generating the raw input data.
		wParam.l			; Same value as wParam of WM_INPUT message
	EndStructure
	
	Structure RAWMOUSE
		usFlags.l
		usButtonFlags.w
		usButtonData.w
		ulRawButtons.l
		lLastX.l
		lLastY.l
		ulExtraInformation.l
	EndStructure
	
	Structure RAWKEYBOARD
		MakeCode.w			; The scan code from the key depression. The scan code for keyboard overrun is KEYBOARD_OVERRUN_MAKE_CODE
		Flags.w				; Flags for scan code information. RI_KEY_BREAK, RI_KEY_MAKE and some others
		Reserved.w		
		VKey.w				; Windows message compatible virtual-key code.
		Message.l			; WM_KEYDOWN, WM_SYSKEYDOWN and so on
		ExtraInformation.l	; Device-specific value
	EndStructure
	
	Structure RAWHID
		dwSizeHid.l
		dwCount.l
		bRawData.b [1]		; this array has variable size, thus structure too
	EndStructure
	
	Structure RAWINPUT
		header.RAWINPUTHEADER
		StructureUnion
			mouse.RAWMOUSE
			keyboard.RAWKEYBOARD
			hid.RAWHID
		EndStructureUnion
	EndStructure
	
	Prototype GetRawInputData(hRawInput, uiCommand, *pData.RAWINPUT, *pcbSize, cbSizeHeader)
	Prototype RegisterRawInputDevices(*pRawInputDevices.RAWINPUTDEVICE, *uiNumDevices, cbSize)
	Prototype GetRawInputDeviceList(*pRawInputDeviceList.RAWINPUTDEVICELIST, *uiNumDevices, cbSize)
	Prototype GetRawInputDeviceInfo(hDevice, uiCommand, *pData, *pcbSize)
	


	; import functions
	Global User32_dll = OpenLibrary(#PB_Any, "user32.dll")
	If IsLibrary(User32_dll)
		Global RegisterRawInputDevices.RegisterRawInputDevices	= GetFunction(User32_dll, "RegisterRawInputDevices")
		Global GetRawInputData.GetRawInputData					= GetFunction(User32_dll, "GetRawInputData")
		Global GetRawInputDeviceList.GetRawInputDeviceList		= GetFunction(User32_dll, "GetRawInputDeviceList")
		Global GetRawInputDeviceInfo.GetRawInputDeviceInfo		= GetFunction(User32_dll, "GetRawInputDeviceInfoW")
	EndIf

;}



; windows message processing
Procedure WinCallback (hWnd, Message, WParam, LParam)
	Select Message 		
		Case #WM_INPUT: ; Raw Input
		
			Protected pcbSize.l
			Protected *pRawData.RAWINPUT
			If GetRawInputData(lParam, #RID_INPUT, #Null, @pcbSize, SizeOf(RAWINPUTHEADER)) = 0
				*pRawData = AllocateMemory(pcbSize) ; If input is from HID-device, data size can vary
				If *pRawData
					GetRawInputData(lParam, #RID_INPUT, *pRawData, @pcbSize, SizeOf(RAWINPUTHEADER))
					Select *pRawData\header\dwType
						Case #RIM_TYPEKEYBOARD:
							; here input is collected
							Debug "Vk " + Str(*pRawData\keyboard\VKey) + " from device " + Str(*pRawData\header\hDevice)
					EndSelect
					
					FreeMemory(*pRawData)
				EndIf
				
			EndIf
	EndSelect
	
	ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure


Procedure Main ()

Protected WndMain = 123
Protected hWnd = OpenWindow(WndMain, #PB_Ignore, #PB_Ignore, 300, 200, "SOME RAW INPUT HOOK", #PB_Window_Invisible)

	; capture window messages
	SetWindowCallback(@WinCallback())
	
	; set raw input capture for all mouses and keyboards
	Dim Rid.RAWINPUTDEVICE(0)
	Rid(0)\usUsagePage  = 1
	Rid(0)\usUsage      = 0
	Rid(0)\hwndTarget   = hWnd
	Rid(0)\dwFlags   	= #RIDEV_INPUTSINK | #RIDEV_PAGEONLY

	If RegisterRawInputDevices (@Rid(), ArraySize(Rid()) + 1, SizeOf(RAWINPUTDEVICE)) = #True
		Debug "Press any key on any keyboard ..."
		
		Repeat
		Until WaitWindowEvent() = #PB_Event_CloseWindow
		
	Else
; 		Msg("Failed to register devices: " + NameLastError())
	EndIf

	If IsLibrary(User32_dll)
		CloseLibrary(User32_dll)
	EndIf
EndProcedure

;;;;;;;;;
Main()
End
Last edited by Lunasole on Sat Sep 24, 2016 3:32 pm, edited 1 time in total.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Multiple keyboards and selective input blocking

Post by Lunasole »

Related offtopic: some really small and really annoying app ^^ Run it at background and leave so...

Code: Select all

EnableExplicit

Repeat
	If Random(100, 0) <= 15
		BlockInput_(#True)
		Delay(Random(256, 8))
		BlockInput_(#False)
	EndIf
	
	Delay(500)
ForEver
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Multiple keyboards and selective input blocking

Post by Lunasole »

Generally what I've got for now, is input handling using that Raw Input and input blocking using RegisterHotKey() API (!). Pff, some really untypical way ^^ Which however, works with limitations (input is blocked for any apps or windows which are not using Raw Input too).

But I really don't see anything else which can work for all system (not only for your thread/process/windows - that is simple and combination of Raw Input + regular keyboard hook works perfect). Except maybe writing something like custom HID driver, which looks seriously complicated to do both for XP and higher versions.

Anyway, the topic still actual (and lot of similar topics I've created on other resources - they also almost have no replies ^^) Is this task really so untrivial?
At least for me it definitely is, I'm close to be out of ideas better than already tried and far from good solution.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Mijikai
Addict
Addict
Posts: 1520
Joined: Sun Sep 11, 2016 2:17 pm

Re: Multiple keyboards and selective input blocking

Post by Mijikai »

U could use/force EnableWindow() to block any input for windowed applications...
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Multiple keyboards and selective input blocking

Post by Lunasole »

Mijikai wrote:U could use/force EnableWindow() to block any input for windowed applications...
If that would be such simply :3
What you described is unacceptable, as well as BlockInput() API I mentioned. Just because it is too late to block input at stage when it is parsed by WM_INPUT message. And you have to depend on Raw input to differentiate one keyboard from another, using data coming with WM_INPUT message.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
thyphoon
Enthusiast
Enthusiast
Posts: 355
Joined: Sat Dec 25, 2004 2:37 pm

Re: Multiple keyboards and selective input blocking

Post by thyphoon »

Thanks for your Code 🥰
Many day i Searsh a solution to use 2 mouses in my game.
You give me the solution 🥳
Post Reply