Ooops
January 26, 2021, 12:56:04 PM

Author Topic: [bmx] Multiple Keyboards Handling by Charrua [ 1+ years ago ]  (Read 894 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
Title : Multiple Keyboards Handling
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: BlitzMax
  1. Strict
  2.  
  3. '       Multiple Keyboard Handling
  4. '       by Juan Ignacio Odriozola
  5. '
  6. '       based on 2 great works:
  7. '               http://www.codeproject.com/KB/system/rawinput.aspx                      << started with
  8. '               http://blitzbasic.com/Community/posts.php?topic=85660#969498    << looking for "how to get a handle to my window find this!!!!
  9. '
  10. '       thak's sswift for your exelent work!!, i used much of your code here!
  11. '
  12. '               probably you better than i, could modify your RAWINPUT.BMX to Registry both KeyBoard and Mice devices
  13. '               and the WinProc to handle both kind of devices (the struct RAWINPUTKeyBoard has one modification from your RAWINPUT)
  14. '
  15. '
  16. '       One Computer, One Screen (probably big), many users: many Keyboards..., many mice too, but not implemented yet!
  17. '       Why not to have a cursor for each mouse instaled, the same for keyboards
  18. '
  19. '       see: http://www.wunderworks.com/
  20. '
  21. '       I test: KeyBoards, NumericKeypads, WirelessPresenters (for PowerPoint and the like)
  22. '       please advice me about other devices
  23. '
  24. '       I will use this idea for manage multiple keypads to get answers from the audience in congress,
  25. '       at this time i have implemented one system of this kind with dedicated microcontroler based hardware
  26. '       (200 kepads), time to time i redesign the system (hard or soft) but now i decided to use something already done!
  27. '
  28. '       i hope that this has an application in game programming
  29. '
  30. 'what the code does:
  31. '       First:  signals the system to send us KeyBoard events (RegisterRID_KEYBOARD(hWnd:Int))
  32. '       Second: Obtain the list of Keyboards on the system    ()
  33. '       wait for keyboard events and show what its received
  34.  
  35.  
  36. 'types and variables
  37.  
  38.  
  39.         Extern "Win32"
  40.                 Function RegisterRawInputDevices:Int(pRawInputDevices:Byte Ptr, uiNumDevices:Int, cbSize:Int)
  41.                 Function GetRawInputData:Int(hRawInput:Byte Ptr, uiCommand:Int, pData:Byte Ptr, pcbSize:Int Ptr, cbSizeHeader:Int)
  42.                 Function GetRawInputDeviceList:Int(pRawInputDeviceList:Byte Ptr, puiNumDevices:Int Ptr, cbSize:Int)
  43.                 Function GetRawInputDeviceInfoA:Int( hDevice:Int, uiCommand:Int, pData:Byte Ptr, pcbSize:Int Ptr)
  44.         End Extern
  45.  
  46.         Global OldWinProc:Int ' This is the old blitzmax WinProc() function pointer.  This is set when you call HookWinProc().  
  47.  
  48.         Const HID_USAGE_PAGE_GENERIC%  = $1
  49.         Const HID_USAGE_GENERIC_MOUSE% = $2
  50.         Const RIDEV_INPUTSINK% = $100
  51.         Const RIM_TYPEMOUSE% = 0
  52.         Const RID_INPUT% = $10000003
  53.        
  54.         Const RIDI_PREPARSEDDATA = $20000005
  55.         Const RIDI_DEVICEINFO = $2000000B
  56.        
  57.         Type RAWINPUTDEVICE
  58.            Field usUsagePage:Short
  59.            Field usUsage:Short
  60.            Field dwFlags:Int
  61.            Field hwndTarget:Int
  62.         End Type
  63.        
  64.         Global Rid:RAWINPUTDEVICE = New RAWINPUTDEVICE
  65.        
  66.         'constants not used in RawInput
  67.         Const HID_USAGE_GENERIC_KEYBOARD% = $6
  68.         Const RIM_TYPEKEYBOARD%  = 1   
  69.         Const RIM_TYPEHID%       = 2   
  70.         Const RIDI_DEVICENAME%   = $20000007
  71.         Const WM_KEYDOWN                = $0100
  72.         Const WM_SYSKEYDOWN     = $0104
  73.         Const WM_INPUT          = $00FF
  74.        
  75.         'RAWINPUT modified
  76.         Type RAWINPUTKeyBoard
  77.                 ' RAWINPUTHEADER:
  78.                         Field dwType:Int
  79.                         Field dwSize:Int
  80.                         Field hDevice:Int               'byte ptr       i changed this!!!!  <<<<<<<<<<<<<<<<<
  81.                         Field WPARAM:Int Ptr
  82.                 ' RAWINPUTKEYBOARD:    
  83.                         Field MakeCode:Short
  84.                         Field Flags:Short
  85.                         Field Reserved:Short
  86.                         Field VKey:Short
  87.                         Field Message:Int
  88.                         Field ExtraInformation:Int
  89.         End Type
  90.        
  91.         Type RID_DEVICE_INFO            '32 bytes in total
  92.                 Field cbSize:Int
  93.                 Field dwType:Int
  94.                 'particulary for keyboards info
  95.                 Field dw_Type:Int
  96.                 Field dwSubType:Int
  97.                 Field dwKeyboardMode:Int
  98.                 Field dwNumberOfFunctionKeys:Int
  99.                 Field dwNumberOfIndicators:Int
  100.                 Field dwNumberOfKeysTotal:Int
  101.         End Type
  102.        
  103.         Type Keyboard
  104.                 Field deviceName:String
  105.                 Field hDevice:Int
  106.                 Field dwType:Int
  107.                 Field ClassCode:String          'sub strings of deviceName
  108.                 Field SubClassCode:String       'Vid= Vendor Id, Pid=Product Id important to find specific hardware!
  109.                 Field Protocol:String          
  110.                 Field Guid:String
  111.                 Field KeyPressed:Int            'has been pressed?
  112.                 Field Key:Short                 'the one that has been if any (ascii code if any)
  113.                 Field ScanCode:Short            'the key number in the keyboard matrix
  114.         End Type
  115.        
  116.         Global lstKeyboards:TList = New TList
  117.         Global KeyboardsCount
  118.         Local Item:Keyboard
  119.  
  120. 'end Types and variables
  121.  
  122. ' from RAWINPUT.BMX
  123. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  124. ' This function registers the KEYBOARD as a raw input device, so that WM_INPUT events will be generated for it.
  125. ' hWnd is the pointer to your window.  See HookWinProc() above for more details.
  126. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  127.  
  128.  
  129.         Function RegisterRID_KEYBOARD(hWnd:Int)
  130.                
  131.                 Rid.usUsagePage = HID_USAGE_PAGE_GENERIC
  132.                 Rid.usUsage = HID_USAGE_GENERIC_KEYBOARD
  133.                 Rid.dwFlags = RIDEV_INPUTSINK
  134.                 Rid.hwndTarget = hWnd
  135.                 RegisterRawInputDevices(Rid, 1, SizeOf(Rid))
  136.  
  137.         End Function
  138.  
  139.  
  140. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  141. '       fills the list: lstKeyboards with the Keyboards find in the system
  142. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  143.  
  144.         Function GetKeyboardList()
  145.        
  146.                 Local Result:Int, i:Int
  147.                 Local hDevice:Int               'field of DeviceInfoList struct not defined
  148.                 Local dwType:Int                'DeviceList should be an Array of DeviceInfoList
  149.                
  150.                 Local uiNumDevices:Int, puiNumDevices:Int Ptr = Varptr uiNumDevices
  151.                 Local cbSize:Int, Data:TBank, pData:Byte Ptr, deviceName:String, split:String[]
  152.                 Local Item:Keyboard
  153.                
  154.                 Result = GetRawInputDeviceList(Null,puiNumDevices,8)    'if first param = null, this func return the number of Devices in the list
  155.                
  156.                 Local List:TBank = CreateBank(uiNumDevices*8)           'strictly an array of unNumberDevices of Type RawInputDeviceList : 2 Int vars: dwType,hDevice
  157.                 Local DeviceList:Byte Ptr = LockBank(List)
  158.                
  159.                 Result = GetRawInputDeviceList(DeviceList, puiNumDevices, 8)    'this second call populates the Array (Bank) wiht the device list
  160.                
  161.                 'the devicename could be used to get additional information from the windows registry
  162.                 'some tipical strings:
  163.                 '       /??/HID#VID_05A4&PID_9840#6&145a460c&0&0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}
  164.                 '       /??/ACPI#PNP0303#4&7989e7a&0#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}
  165.                 '
  166.                 'the "#" character separates "fields" inside that devicename
  167.                 'the following code, separates the DeviceName in 4 Fields, they may be used concatenated later to reach the registry
  168.                 'or individually to find the VendorID and ProductID if you want to manage specific hardware
  169.                 '
  170.                 ' HKEY_LOCAL_MACHINESYSTEMCurrentControlSetEnum ClassCode  SubClassCode  Protocol       'guid isnt needed!
  171.                 ' the Class key confirm if it is a KeyBoard
  172.                 '{ "HKEY_LOCAL_MACHINESYSTEMCurrentControlSetEnum" + Item.ClassCode + "" + ItemSubClassCode + "" + Item.Protocol }
  173.                 '
  174.                 'more about Registry    (i guess you have to have some administrator level to reach this variables from code
  175.                 'please, i some one could reach them, post the solution, i used the following code wihtout success
  176.                 '
  177.                
  178.                 'get the devicename of each device
  179.                
  180.                 For i=0 To uiNumDevices-1
  181.                         hDevice = PeekInt(List,i*8+0)   'calculates the offset inside the DeviceList of the individual items
  182.                         dwType  = PeekInt(List,i*8+4)
  183.                                                
  184.                         GetRawInputDeviceInfoA( hDevice, RIDI_DEVICENAME, Null, Varptr cbSize)  'get the lenght of the DeviceName string in cbSize
  185.                
  186.                         If cbSize>0 Then
  187.                
  188.                                 Data = CreateBank(cbSize)       'allocates cbSize bytes to hold the DeviceName string
  189.                                 pData= LockBank(Data)
  190.                                 GetRawInputDeviceInfoA( hDevice, RIDI_DEVICENAME, pData, Varptr cbSize) 'second call to get the DeviceName in pData
  191.                                 DeviceName = String.frombytes(pData,cbSize)
  192.                                 'pad the firs 4 "??"
  193.                                 DeviceName = Right$(DeviceName,Len(DeviceName)-4)
  194.                                 Local temp:Int = DeviceName.find("}",1)
  195.                                 DeviceName=Left$(DeviceName,temp+1)
  196.                                 UnlockBank(Data)
  197.  
  198.                                 Print "Detected: "+String.fromint(hDevice)+", "+DeviceName
  199.  
  200.                                 If dwType=RIM_TYPEKEYBOARD Then 'only keyboards
  201.                                        
  202.                
  203.                                         If (deviceName.toupper()).find("ROOT")=-1 Then  'exclude Root device
  204.                                                 Print "      Added to list: "
  205.                                                 KeyboardsCount = KeyboardsCount + 1
  206.                                                 split = devicename.split("#")
  207.                                                 Item = New Keyboard
  208.                                                 lstKeyboards.AddLast(Item)
  209.                                                 Item.hDevice = hDevice
  210.                                                 Item.dwType  = dwType
  211.                                                 Item.deviceName = DeviceName
  212.                                                 item.ClassCode = split[0]
  213.                                                 item.SubClassCode = split[1]
  214.                                                 item.Protocol = split[2]
  215.                                                 item.Guid = split[3]
  216.                                                 Item.KeyPressed=False
  217.                                                
  218.                                         Else
  219.                                                 Print "root device: "+devicename       
  220.                                         End If
  221.                                 End If
  222.                         End If
  223.                 Next
  224.                
  225.                 UnlockBank(List)
  226.                
  227.         End Function
  228.  
  229.  
  230. ' from RAWINPUT.BMX
  231.  
  232. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  233. ' hWnd is the pointer to your window.  hWnd is not the gadget pointer!
  234. ' You can get hWnd with hWnd = GetActiveWindow(), or with hWnd = QueryGadget(Window, QUERY_HWND), where Window is your window's gadget pointer.
  235. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  236.  
  237.         Function HookWinProc(hWnd:Int)
  238.                 Local GWL_WNDPROC:Int = -4
  239.                 OldWinProc = SetWindowLongA(hWnd, GWL_WNDPROC, Int(Byte Ptr(WinProc)))
  240.         End Function  
  241.  
  242.  
  243. ' from RAWINPUT.BMX but with modifications.
  244. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  245. ' This function is called automatically.  It grabs raw windows events before Blitzmax processes them, so that you can handle events that Blitzmax doesn't provide an interface for.
  246. '
  247. ' Blitzmax has its own function like this internally, and this one calls that when it is done processing.
  248. ' Presumably, events handled by this function will still be passsed onto BlitzMax, so avoid processing the same data in both event handlers!
  249. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  250.  
  251.         Function WinProc:Int(hWnd:Int, Msg:Int, wParam:Int, lParam:Int) "win32"
  252.        
  253.                 Const WM_INPUT% = $00FF
  254.        
  255.                 Function LWord:Short(I:Int) ' Returns the lower 16 bits of a 32 bit integer.
  256.                         Return I & $FFFF
  257.                 End Function
  258.        
  259.                 Function HWord:Short(I:Int) ' Returns the upper 16 bits of a 32 bit integer.
  260.                         Return I Shr 16
  261.                 End Function
  262.                
  263.                
  264.                 Select Msg
  265.        
  266.                     Case WM_INPUT
  267.  
  268.                                 Local dwSize:Int = 32
  269.                                 Local Raw:RAWINPUTKeyBoard = New RAWINPUTKeyBoard
  270.                                 Local Item:Keyboard
  271.    
  272.                                 GetRawInputData(Byte Ptr(lParam), RID_INPUT, Raw, Varptr dwSize, 16) ' Get data in RAWINPUT structure.
  273.  
  274.                                 If Raw.dwType = RIM_TYPEKEYBOARD
  275.                                         If (raw.Message = WM_KEYDOWN Or raw.Message = WM_SYSKEYDOWN) Then
  276.                                                 If raw.vkey < 254 Then
  277.                                                         For Item:Keyboard = EachIn lstKeyboards
  278.                                                                 If raw.hDevice = Item.hDevice Then
  279.                                                                         Item.KeyPressed = True
  280.                                                                         Item.Key = raw.VKey
  281.                                                                         Item.ScanCode = raw.MakeCode
  282.                                                                         Print String.fromint(raw.hDevice)+", "+String.fromint(raw.MakeCode)+", "+String.fromint(raw.VKey)+", KeyPressed: ("+Chr(raw.vkey)+")" + Item.DeviceName
  283.                                                                         Exit
  284.                                                                 End If
  285.                                                         Next
  286.                                                 Else
  287.                                                         'DebugLog("extended code")
  288.                                                 End If
  289.                                         End If
  290.                                 EndIf
  291.                                
  292.                         'Default
  293.                         '       DebugLog("Unknown Msg: " + Msg)
  294.                                        
  295.                 End Select
  296.        
  297.                 If OldWinProc <> 0 Then Return CallWindowProcA(Byte Ptr(OldWinProc), hWnd, Msg, wParam, lParam)  
  298.                
  299.         End Function
  300.  
  301. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  302. '       MAIN PROGRAM
  303. ' ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  304.  
  305.  
  306. Graphics 640,480
  307.  
  308. Local hWnd:Int
  309.  
  310. hWnd = GetActiveWindow()
  311.  
  312. 'First
  313. RegisterRID_KEYBOARD(hWnd)
  314.  
  315. 'Second
  316. GetKeyboardList()
  317.  
  318.  
  319. Print "Keyboards List:"
  320. Print
  321. Print "Number of keyboards detected: "+KeyboardsCount
  322.  
  323. For Item:Keyboard = EachIn lstKeyboards
  324.         Print "hDevice: "+String.fromint(item.hDevice)+" = " + item.ClassCode + "" + item.SubClassCode + "" + item.Protocol + "" + item.Guid
  325. Next
  326. Print
  327.  
  328. 'now and finally: RawInput!
  329.  
  330. HookWinProc(hWnd)
  331.  
  332. Local char:Int
  333.  
  334. 'one important note: with or without the DeviceList, GetRawInputData knows de hDevice, so identification is posible, but
  335. 'without the list, we haven't the knowledge of which devices are installed, if one doesn't press a key, will never appear in
  336. 'hDevice from GetRawInputData.
  337.  
  338. While Not KeyHit(key_escape)
  339.         'really not much here!!
  340.         'ScanCodes are better, when special keys are used: F1, NumPadKeys etc
  341.         char = GetChar()
  342.         If char Then Print "in the main loop, blitz :"+ String.fromint(char)
  343.         'as you see, blitz still get the characters, windows combine the imput of all the HID's in the system
  344.         'but looking in the list of KeyBoards we can identify from who the hit were done
  345.         For Item:Keyboard = EachIn lstKeyboards
  346.                 If Item.KeyPressed Then
  347.                         Item.KeyPressed = False
  348.                         Print "Identified source: "+String.fromint(Item.hDevice)+", "+String.fromint(Item.ScanCode)+", "+String.fromint(Item.Key)
  349.                         Exit
  350.                 End If
  351.         Next
  352. Wend
  353.  
  354. End


Comments : none...

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal