[bb] Multiple Keyboards Handling, Blitz3D version by Charrua [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:41

Previous topic - Next topic

BlitzBot

Title : Multiple Keyboards Handling, Blitz3D version
Author : Charrua
Posted : 1+ years ago

Description : 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


Code :
Code (blitzbasic) Select
; 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
varRidhWnd = hWnd
varRidFlags = 256
varRidUsage = $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 ddwType = RIM_TYPEKEYBOARD Then ;we only want Keyboards
If dClassCode<>"ROOT" Then ;and not The Root device
k = New tKeyboard ;simply translate the info from Device to Keyboard
khDevice = dhDevice ;this is the ID from wich we identify the keyboard
kdwType = ddwType
kDeviceName = ddeviceName
kClassCode = dClassCode$
kSubClassCode = dSubClassCode$
kProtocol = dProtocol$
kGuid = dGuid$
kDeviceNameAsIs = dDeviceNameAsIs
End If
End If
Next

For k=Each tKeyboard
Print khDevice
Print kDeviceName
Print kClassCode
Print kSubClassCode
Print kProtocol
Print kGuid
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, khDevice + ": "+Asc(kKey)+", Scan:"+kScanCode
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(RawMessage) Then
gwParam = LWord(RawMessage)
glParam = LWord(RawReservedVKey)
End If

If ( LWord(RawMessage = WM_KeyDown) Or LWord(RawMessage = WM_SYSKEYDOWN) ) Then
If HWord(RawReservedVKey) < 254 Then
For Item = Each tKeyboard
If rawhDevice = ItemhDevice Then
ItemKeyPressed = True
ItemKey = HWord(RawReservedVKey)
ItemScanCode = LWord(RawMakeCodeFlags)
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
ddeviceName = DeviceName
dhDevice = hDevice
ddwType = dwType
dClassCode$ = Parte1
dSubClassCode$ = Parte2
dProtocol$ = Parte3
dGuid$ = Parte4
dDeviceNameAsIs = 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...