Code archives/User Input/Multiple Keyboards Handling, Blitz3D version

This code has been declared by its author to be Public Domain code.

Download source code

Multiple Keyboards Handling, Blitz3D version by Charrua2010
If you have more than one keyboard, a numeric keypad for instance, or, a notebook with a keyboard connected,
you can enter the number "123" typing one digit on each diferent keyboard and the application receives "123".
Windows normal handling gives you the input but you don't know from where. It should become form a "Remote presenter
for power point".

This code scans the list of keyboards and get handles to them, registers the application to receive keyboard input
and then, windows call us, every time a key is pressed and calling the apropiate RawInput function we get the Handle
of the keyboard propietary of that keystroke.

No graphical interface, only Print instructions, first appears a list of detected devices (input, hid) some are
filtered, and finally appears the list of "Listed Devices"
press a key, and an Id appears and the code received.

Blitz still receives the char, so you can use normal blitz functions to read keyboard.

Hope you may found this useful.

Juan
;	Multiple Keyboard Handling
;	by Juan Ignacio Odriozola
;
;	based on 2 great works:
;		http://www.codeproject.com/KB/system/rawinput.aspx			<< started with
;		http://blitzbasic.com/Community/posts.php?topic=85660#969498	<< looking for "how to get a handle to my window find this!!!!
;
;	thak's sswift for your exelent work!!, i used much of your code here!
;
;
;
;	One Computer, One Screen (probably big), many users: many Keyboards..., many mice too, but not implemented yet!
;	Why not to have a cursor for each mouse instaled, the same for keyboards
;
;	see: http://www.wunderworks.com/
;
;	I test: KeyBoards, NumericKeypads, WirelessPresenters (for PowerPoint and the like)
;	please advice me about other devices
;
;	I will use this idea for manage multiple keypads to get answers from the audience in congress,
;	at this time i have implemented one system of this kind with dedicated microcontroler based hardware
;	(200 kepads), time to time i redesign the system (hard or soft) but now i decided to use something already done!
;
;	i hope that this has an application in game programming
;
;what the code does:
;	First: 	signals the system to send us KeyBoard events (RegisterRID_KEYBOARD(hWnd:Int))
;	Second:	Obtain the list of Keyboards on the system    ()
;	wait for keyboard events and show what its received
;
;the bmax version is documented better
;
;needs some decls And the free FastPointer lib
;
;all came from User32.decls
;
;
;probably you have the original ones, there are some diferences depending on usage, so the postfix 1, 2..

;api_GetRawInputDeviceList%(pRawInputDeviceList*, uiNumDevices*, cbSize%) : "GetRawInputDeviceList"	;<====  this is called
;api_GetRawInputDeviceList1%(pRawInputDeviceList, uiNumDevices*, cbSize%) : "GetRawInputDeviceList"	;<====  this is called

;api_GetRawInputDeviceInfo%(hDevice%, uiCommand%, pData*, pcbSize%) : "GetRawInputDeviceInfoA"		;		not needed
;api_GetRawInputDeviceInfo1%(hDevice%, uiCommand%, pData%, pcbSize*) : "GetRawInputDeviceInfoA"		;<====  this is called
;api_GetRawInputDeviceInfo2%(hDevice%, uiCommand%, pData*, pcbSize*) : "GetRawInputDeviceInfoA"		;<====  this is called

;api_RegisterRawInputDevices%(pRawInputDevice*, uiNumDevices%, cbSize%) : "RegisterRawInputDevices"	;<====  this is called

;api_GetRawInputData%(hRawInput*, uiCommand%, pData*, pcbSize*, cbSizeHeader%) : "GetRawInputData"	;<====  not needed
;api_GetRawInputData1%(hRawInput%, uiCommand%, pData*, pcbSize*, cbSizeHeader%) : "GetRawInputData"	;<====  this is called

;api_SetWindowLong% (hwnd%, nIndex%, dwNewLong%) : "SetWindowLongA"									;<====  this is called
;api_CallWindowProc% (lpPrevWndFunc%, hWnd%, Msg%, wParam%, lParam%) : "CallWindowProcA"			;<====  this is called

Graphics 640,480,0,2

; ----------------------------------------------------------------------------------------------
;	constants
; ----------------------------------------------------------------------------------------------


Const RIDI_DEVICEINFO%	= $2000000B
Const RID_INPUT 		= $10000003

Const WM_KEYDOWN		= $0100
Const WM_SYSKEYDOWN		= $0104
Const WM_INPUT			= $00FF

Const RIDI_DEVICENAME% 	= $20000007

Const RIM_TYPEMOUSE% 	= 0
Const RIM_TYPEKEYBOARD% = 1	
Const RIM_TYPEHID%		= 2	

; ----------------------------------------------------------------------------------------------
;	type def
; ----------------------------------------------------------------------------------------------

Type tDevice
	Field DeviceNameAsIs$
	Field deviceName$		;sin los 4 del principio: \\?\
	Field hDevice
	Field dwType
	Field ClassCode$		;
	Field SubClassCode$
	Field Protocol$
	Field Guid$
End Type


Type tKeyboard
	Field deviceName$
	Field hDevice
	Field dwType
	Field ClassCode$
	Field SubClassCode$
	Field Protocol$
	Field Guid$
	Field DeviceNameAsIs$
	Field KeyPressed
	Field Key			;estas deberian ser Short!
	Field ScanCode
End Type

Type tRawIK	;Raw Input Keyboard		;c programmers uses unions, here the union is by fact.
	
	;RAWINPUTHEADER:	;first 16 bytes
		Field dwType
		Field dwSize
		Field hDevice
		Field WPARAM
	;RAWINPUTKEYBOARD:	;struct for keyboard
		Field MakeCodeFlags
		;Field Flags
		Field ReservedVKey
		;Field VKey
		Field Message
		Field ExtraInformation
End Type 

Type tRID	;Raw Input Device
	Field Usage	;blitz don't have 16 variables, in this field are 2 16 variables, be careful
				;the hight word must be $0001, the low word 0006 to register keyboard input
	Field Flags 
	Field hWnd	;my window
End Type


;------------------------------------------------------------------------------------------------


;Global raw.tRawIK = New tRawIK


Global OldWinProc								;old handler

Local hWnd = SystemProperty("AppHWND")				;my window

Global WinProcPointer = FunctionPointer()		;our_handler, we call old_handler from our handler to continue the chain
	Goto skip
	WinProc(0,0,0,0)
	.skip

varRid.tRID = New tRID
varRid\hWnd = hWnd
varRid\Flags = 256
varRid\Usage = $00060001

;------------------------------------------------------------------------------------------------
;	say: i want to receive raw input, please!
;------------------------------------------------------------------------------------------------

Local Result = api_RegisterRawInputDevices(varRid,1,12)

;------------------------------------------------------------------------------------------------
;	ok, tell me wich of your Funtions i should call
;------------------------------------------------------------------------------------------------

If Result Then HookWinProc(hWnd)

;------------------------------------------------------------------------------------------------


Print "RegisterRawInputDevices: "+Result


Local k.tKeyboard

;------------------------------------------------------------------------------------------------
;	Get the list of devices
;------------------------------------------------------------------------------------------------

nDevices = GetDeviceList()	;populates a list of tDevice objects

If nDevices Then

	For d.tDevice = Each tDevice
		If d\dwType = RIM_TYPEKEYBOARD Then	;we only want Keyboards 
			If d\ClassCode<>"ROOT" Then		;and not The Root device
				k = New tKeyboard			;simply translate the info from Device to Keyboard
				k\hDevice = d\hDevice		;this is the ID from wich we identify the keyboard
				k\dwType = d\dwType
				k\DeviceName = d\deviceName
				k\ClassCode = d\ClassCode$
				k\SubClassCode = d\SubClassCode$
				k\Protocol = d\Protocol$
				k\Guid = d\Guid$
				k\DeviceNameAsIs = d\DeviceNameAsIs
			End If
		End If
	Next		

	For k=Each tKeyboard
		Print k\hDevice
		Print k\DeviceName
		Print k\ClassCode
		Print k\SubClassCode
		Print k\Protocol
		Print k\Guid
		Print 
	Next
	
	Print hWnd + " - " + OldWinProc
		
End If

Print
Print "press any key...."
WaitKey		;this is only to see the list of detected keyboards


;in the main loop we see the ID, ascii and scan code for each device

If nDevices Then

	While Not KeyHit(1)
	
		Cls

		n=0
		For k=Each tKeyboard
			Text 30,30+n*20, k\hDevice + ": "+Asc(k\Key)+", Scan:"+k\ScanCode
			n=n+1
		Next
			
	Wend
	
End If

End


; ----------------------------------------------------------------------------------------------
; hWnd is the pointer To your window.
; ----------------------------------------------------------------------------------------------

Function HookWinProc(hWnd)
	Local GWL_WNDPROC = -4
	OldWinProc = api_SetWindowLong(hWnd, GWL_WNDPROC, WinProcPointer)
End Function  


; ----------------------------------------------------------------------------------------------
; This Function is called automatically.
; ----------------------------------------------------------------------------------------------


Function WinProc( hWnd, Msg, wParam, lParam )

	Local Item.tKeyboard
	Local raw.tRawIK = New tRawIK
	
	Select Msg

		Case WM_INPUT
		
			SizeBank = CreateBank(4)
			PokeInt SizeBank,0,32
			
			Result = api_GetRawInputData1(lParam, RID_INPUT, raw, SizeBank, 16)
			
			FreeBank SizeBank
			
			If LWord(Raw\Message) Then
				gwParam = LWord(Raw\Message)
				glParam = LWord(Raw\ReservedVKey)
			End If

			If ( LWord(Raw\Message = WM_KeyDown) Or LWord(Raw\Message = WM_SYSKEYDOWN) ) Then
				If HWord(Raw\ReservedVKey) < 254 Then
					For Item = Each tKeyboard
						If raw\hDevice = Item\hDevice Then
							Item\KeyPressed = True
							Item\Key = HWord(Raw\ReservedVKey)
							Item\ScanCode = LWord(Raw\MakeCodeFlags)
						End If
					Next
				End If
			End If
			
	End Select
	
	Delete raw
	
	If OldWinProc <> 0 Then
		Return api_CallWindowProc(OldWinProc, hWnd, Msg, wParam, lParam)
	End If
		
End Function

;--------------------------------------------------------------------------------


Function GetDeviceList()

	Local dCount, Result, DeviceList, Count
	Local i, ii, hDevice, dwType, cbSizeBank, cbSize
	Local pData, DeviceName$, Ultimo, Primero, Parte1$, Parte2$, Parte3$, Parte4$
	Local Pos, Root, DeviceNameAsIs$
	Local d.tDevice
	Local DeviceCount = CreateBank(4)
	
	
	PokeInt DeviceCount,0,0
	Result = api_GetRawInputDeviceList1(0,DeviceCount,8)
	
	If Result<>-1 Then
		DeviceList = CreateBank(DeviceCount*8)
		Count = api_GetRawInputDeviceList(DeviceList,DeviceCount,8)
		For i=0 To Count-1
			hDevice = PeekInt(DeviceList,i*8)
			dwType = PeekInt(DeviceList,i*8+4)
			cbSizeBank=CreateBank(4)
			api_GetRawInputDeviceInfo1(hDevice, RIDI_DEVICENAME, 0, cbSizeBank) 
			cbSize = PeekInt(cbSizeBank,0)
			If (cbSize > 0) Then
				pData = CreateBank(cbSize)
				Result = api_GetRawInputDeviceInfo2(hDevice, RIDI_DEVICENAME, pData, cbSizeBank)
				cbSize = PeekInt(cbSizeBank,0)
				DeviceName$ = ""
				For ii=0 To cbSize-1
					DeviceName = DeviceName + Chr(PeekByte(pData,ii))
				Next
				FreeBank pData
				
				Pos = Instr(DeviceName,"}",1)
				If Pos Then DeviceName = Left(DeviceName,Pos)
				DeviceNameAsIs$ = DeviceName
				DeviceName$ = Upper(Right(DeviceName,Len(DeviceName)-4))
				Root = Instr(DeviceName,"ROOT",1)
				Ultimo=0
				Primero=Ultimo+1
				Ultimo=Instr(DeviceName,"#",Primero)
				Parte1$ = Mid(DeviceName,Primero,Ultimo-Primero)
				Primero=Ultimo+1
				Ultimo=Instr(DeviceName,"#",Primero)
				Parte2$ = Mid(DeviceName,Primero,Ultimo-Primero)
				Primero=Ultimo+1
				Ultimo=Instr(DeviceName,"#",Primero)
				Parte3$ = Mid(DeviceName,Primero,Ultimo-Primero)
				Primero=Ultimo+1
				Ultimo=Instr(DeviceName,"#",Primero)
				Parte4$ = Mid(DeviceName,Primero,Ultimo-Primero)
				dCount = dCount+1
				d.tDevice = New tDevice
				d\deviceName = DeviceName
				d\hDevice = hDevice
				d\dwType = dwType
				d\ClassCode$ = Parte1
				d\SubClassCode$ = Parte2
				d\Protocol$ = Parte3
				d\Guid$ = Parte4
				d\DeviceNameAsIs = DeviceNameAsIs
				
			End If
			FreeBank cbSizeBank
		Next
		
		FreeBank DeviceList
		FreeBank DeviceCount
		Return dCount
		
	Else
		
		FreeBank DeviceCount
		Return -1
		
	End If
	
End Function


;--------------------------------------------------------------------------------

Function LWord(I) ; Returns the Lower 16 bits of a 32 bit integer.
	Return I And $FF
End Function

Function HWord(I) ; Returns the Upper 16 bits of a 32 bit integer.
	Return I Shr 16
End Function

;--------------------------------------------------------------------------------

Comments

None.

Code Archives Forum