Seite 1 von 1

HID - abfrage eines Joysticks - HidD_GetInputReport problem

Verfasst: 12.11.2018 21:53
von GPI
Die Joystickroutinen von PB sind ja nicht so prall (bspw. tauchen XINPUT-Geräte 2x auf), ich versuch gerade das ganze anders zu lösen. Zumal ich die Eingaben immer brauche, auch wenn das Fenster nicht im Focus ist.
XINPUT hat das Problem, das nur XBOX-Controller abgefragt werden können und nicht bspw. PS4- Controller.

Ich hab jetzt 1 1/2 Lösungen. Eine über die RAW-DATA-Routinen von Windows, die klappt wunderbar in 32Bit und 64Bit.
Jetzt wollte ich noch den Schritt machen und direkt auf abfrage abzufragen, das klappt aber überhaupt nicht.
HIDD_GetInputReport funktioniert nicht und liefert nie eine brauchbare Information.

Hat jemand brauchbaren Code, wie man die Buttons und Achsen nur mittels HID abfragen kann?

Code: Alles auswählen

;https://www.codeproject.com/Articles/185522/Using-the-Raw-Input-API-to-Process-Joystick-Input
;https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types
;https://www.codeproject.com/Articles/192941/HID-Application-Class-for-Easy-Reading-of-Joystick
;https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
;https://katyscode.wordpress.com/2013/08/30/xinput-tutorial-part-1-adding-gamepad-support-to-your-windows-game/

DeclareModule joy
  EnableExplicit
  
  #HIDID_PS4     = $09CC0000054C
  #HIDID_XBOXONE = $02FF0000045E
  #HIDID_XBOX360 = $028E0000045E
  
  Structure _Axis_Info
    exist.i
    value.i
  EndStructure
  Structure _Axis
    x._Axis_Info
    y._Axis_Info
    z._Axis_Info
    rx._Axis_Info
    ry._Axis_Info
    rz._Axis_Info
    Slider._Axis_info
    Dial._Axis_Info
    Wheel._Axis_Info
  EndStructure
  Structure _DPad
    exist.i
    x.i
    y.i
  EndStructure
  Structure _Button
    Number.i
    Value.l
  EndStructure
  Structure _HID
    VendorId.l
    ProductId.l
  EndStructure
  Structure sJoystick
    Name.s
    StructureUnion
      HID._HID
      HIDID.Q
    EndStructureUnion
    StructureUnion
      Axis._Axis
      AxisNb._Axis_Info[9]
    EndStructureUnion
    DPad._DPad
    Button._Button
  EndStructure
  
  
  Declare init()
  Declare exit()
  Declare scan();Search for devices
  Declare Register(WindowNb,without_focus=#False)
  Declare CheckEvent(WindowEvent)
  
  Declare EnableRemapPS4(flag)
  
  Declare Enumerate(List Handle.i())
  Declare LastState(handle);return *joystick.joystick
  
  Declare Examine(hDevice)
  
EndDeclareModule


Module Joy
  Procedure.s ShowError ()
    Protected error, *MemoryID,length,e$
    error = GetLastError_ ()
    If error
      *MemoryID = AllocateMemory (255)
      length = FormatMessage_ (#FORMAT_MESSAGE_FROM_SYSTEM, #Null, error, 0, *MemoryID, 255, #Null)
      If length > 1 ; Some error messages are "" + Chr (13) + Chr (10)... stoopid M$... :(
        e$ = PeekS (*MemoryID, length - 2)
      Else
        e$ = "Unknown error!"
      EndIf
      FreeMemory (*MemoryID)
      ProcedureReturn ""+error+" "+e$
    Else
      ProcedureReturn "No error has occurred!"
    EndIf
  EndProcedure 
  
  
  Declare Parse(*PreparseData,hDevice,*report,reportLen,hidhandle=0)
  
  Global DLLuser32, DLLhid
  Global PS4Remap
  ;   
  ; Case $30: *axis=Joysticks()\Joystick\Axis\x
  ; Case $31: *axis=Joysticks()\Joystick\Axis\y
  ; Case $32: *axis=Joysticks()\Joystick\Axis\z
  ; Case $33: *axis=Joysticks()\Joystick\Axis\rx
  ; Case $34: *axis=Joysticks()\Joystick\Axis\ry                            
  ; Case $35: *axis=Joysticks()\Joystick\Axis\rz
  ; Case $36: *axis=Joysticks()\Joystick\Axis\Slider 
  
  Macro PS4AxisRemap(d)
    If ps4remap
      If d=$32
        d=$33
      ElseIf d=$33
        d=$32
      ElseIf d=$35
        d=$34
      ElseIf d=$34
        d=$35
      EndIf
    EndIf    
  EndMacro
  Macro PS4ButtonRemap(d)
    If ps4remap
      Select d
        Case 1:d=0
        Case 2:d=1
          ;3=3
        Case 0:d=2
        Case 9:d=7
        Case 13:d=6
        Case 11:d=9
        Case 10:d=8
          ;4=4
          ;5=5
        Case 6:d=$FF
        Case 7:d=$FF
        Case 8:d=13          
      EndSelect
    EndIf
  EndMacro
  
  
  
  
  ;{ HID-Prototype 
  Prototype.l prot_GetRawInputDeviceList (pRawInputDeviceList.i,puiNumDevices.i,cbSize.l)
  Prototype.l prot_GetRawInputDeviceInfoW (hDevice.i,uiCommand.l,pData.i,pcbSize.i)
  Prototype.l prot_RegisterRawInputDevices (pRawInputDevices.i,uiNumDevices.l,cbSize.l)
  Prototype.l prot_GetRawInputData (hRawInput.i,uiCommand.l,pdata.i,pcbSize.i,cbSizeHeader.l)
  Prototype.l prot_HidP_GetCaps (pPreparsedData.i,pCapabilities.i)
  Prototype.l prot_HidP_GetButtonCaps (ReportType.l, pButtonCaps.i, puiButtonCapsLength.i, pPreparsedData.i)
  Prototype.l prot_HidP_GetValueCaps (ReportType.l, pValueCaps.i , puiValueCapsLength.i , pPreparsedData.i)
  Prototype.l prot_HidP_GetUsages (ReportType.l, wUsagePage.w , uLinkCollection.u, pUsageList.i, plUsageLength.i, pPreparsedData.i, pcReport.i, lReportLength.l)
  Prototype.l prot_HidP_GetUsageValue (ReportType.l, wUsagePage.w , ULinkCollection.u, wUsage.w, pulValue.i, pPreparsedData.i, pcReport.i, lReportLength.l)
  Prototype.l prot_HidP_GetScaledUsageValue (ReportType.l, wUsagePage.w , ULinkCollection.u, wUsage.w, pulValue.i, pPreparsedData.i, pcReport.i, lReportLength.l)
  
  Prototype.l prot_HidP_InitializeReportForID (ReportType.l,ReportID.a,pPreparsedData.i, pcReport.i, lReportLength.l)
  
  Prototype.b prot_HidD_GetProductString (HidDeviceObject.i, pBuffer.i, BufferLength.l)
  Prototype.b prot_HidD_GetPreparsedData (HidDeviceObject.i, pPreparsedData.i)
  Prototype.b prot_HidD_FreePreparsedData (PreparsedData.i)
  Prototype.b prot_HidD_GetInputReport (HidDeviceObject.i, pReportBuffer.i, lReportBufferLength.l)
  
  Global GetRawInputDeviceList.prot_GetRawInputDeviceList
  Global GetRawInputDeviceInfo.prot_GetRawInputDeviceInfoW
  Global RegisterRawInputDevices.prot_RegisterRawInputDevices
  Global GetRawInputData.prot_GetRawInputData    
  
  Global HidP_InitializeReportForID.prot_HidP_InitializeReportForID
  Global HidP_GetCaps.prot_HidP_GetCaps
  Global HidP_GetButtonCaps.prot_HidP_GetButtonCaps
  Global HidP_GetValueCaps.prot_HidP_GetValueCaps
  Global HidP_GetUsages.prot_HidP_GetUsages
  Global HidP_GetUsageValue.prot_HidP_GetUsageValue
  Global HidP_GetScaledUsageValue.prot_HidP_GetScaledUsageValue
  
  Global HidD_GetProductString.prot_HidD_GetProductString
  Global HidD_GetPreparsedData.prot_HidD_GetPreparsedData
  Global HidD_FreePreparsedData.prot_HidD_FreePreparsedData
  Global HidD_GetInputReport.prot_HidD_GetInputReport
  ;}
  
  #HID_Error=$10000000
  
  #Buf256Size=256*2;widechar
  Global *buf256
      
  ;{ HID-Structures
  Structure RAWINPUTDEVICELIST_ Align #PB_Structure_AlignC
    hDevice.i
    dwType.l
  EndStructure
  Macro RAWINPUTDEVICELIST:RAWINPUTDEVICELIST_:EndMacro
  
  Structure RID_DEVICE_INFO_HID_ Align #PB_Structure_AlignC
    dwVendorId.l
    dwProductId.l
    dwVersionNumber.l
    usUsagePage.w
    usUsage.w
  EndStructure
  Macro RID_DEVICE_INFO_HID:RID_DEVICE_INFO_HID_:EndMacro
  
  Structure RID_DEVICE_INFO_KEYBOARD_ Align #PB_Structure_AlignC
    dwType.l
    dwSubType.l
    dwKeyboardMode.l
    dwNumberOfFunctionKeys.l
    dwNumberOfIndicators.l
    dwNumberOfKeysTotal.l
  EndStructure
  Macro RID_DEVICE_INFO_KEYBOARD:RID_DEVICE_INFO_KEYBOARD_:EndMacro
  
  Structure RID_DEVICE_INFO_MOUSE_ Align #PB_Structure_AlignC
    dwId.l
    dwNumberOfButtons.l
    dwSampleRate.l
    fHasHorizontalWheel.l
  EndStructure
  Macro RID_DEVICE_INFO_MOUSE:RID_DEVICE_INFO_MOUSE_:EndMacro
  
  Structure RID_DEVICE_INFO_ Align #PB_Structure_AlignC
    cbSize.l
    dwType.l
    StructureUnion
      mouse.RID_DEVICE_INFO_MOUSE_
      keyboard.RID_DEVICE_INFO_KEYBOARD_
      hid.RID_DEVICE_INFO_HID_
    EndStructureUnion
  EndStructure
  Macro RID_DEVICE_INFO:RID_DEVICE_INFO_:EndMacro
  
  Structure RAWINPUTDEVICE_ Align #PB_Structure_AlignC
    usUsagePage.w
    usUsage.w
    dwFlags.l
    hwndTarget.i
  EndStructure
  Macro RAWINPUTDEVICE:RAWINPUTDEVICE_:EndMacro
  
  Structure RAWINPUTHEADER_ Align #PB_Structure_AlignC
    dwType.l
    dwSize.l
    hDevice.i
    wParam.i
  EndStructure
  Macro RAWINPUTHEADER:RAWINPUTHEADER_:EndMacro
  
  Structure RAWMOUSE_BUTTON_ Align #PB_Structure_AlignC
    usButtonFlags.w
    usButtonData.w
  EndStructure
  Macro RAWMOUSE_BUTTON:RAWMOUSE_BUTTON_:EndMacro
  
  Structure RAWMOUSE_ Align #PB_Structure_AlignC
    usFlags.w
    StructureUnion
      ulButtons.l
      usButton.RAWMOUSE_BUTTON
    EndStructureUnion
    ulRawButtons.l
    lLastX.l
    lLastY.l
    ulExtraInformation.l
  EndStructure
  Macro RAWMOUSE:RAWMOUSE_:EndMacro
  
  Structure RAWKEYBOARD_ Align #PB_Structure_AlignC
    MakeCode.w
    Flags.w
    Reserved.w
    VKey.w
    Message.l
    ExtraInformation.l
  EndStructure
  Macro RAWKEYBOARD:RAWKEYBOARD_:EndMacro
  
  Structure RAWHID_ Align #PB_Structure_AlignC
    dwSizeHid.l
    dwCount.l
    bRawData.b[1]
  EndStructure
  Macro RAWHID:RAWHID_:EndMacro
  
  Structure RAWINPUT_ Align #PB_Structure_AlignC
    header.RAWINPUTHEADER
    StructureUnion
      mouse.RAWMOUSE
      keyboard.RAWKEYBOARD
      hid.RAWHID
    EndStructureUnion
  EndStructure
  Macro RAWINPUT:RAWINPUT_:EndMacro
  
  Structure HIDP_CAPS  Align #PB_Structure_AlignC
    UsagePage.w
    Usage.w    
    InputReportByteLength.u
    OutputReportByteLength.u
    FeatureReportByteLength.u
    Reserved.u[17]           
    NumberLinkCollectionNodes.u
    NumberInputButtonCaps.u    
    NumberInputValueCaps.u     
    NumberInputDataIndices.u   
    NumberOutputButtonCaps.u   
    NumberOutputValueCaps.u    
    NumberOutputDataIndices.u  
    NumberFeatureButtonCaps.u  
    NumberFeatureValueCaps.u   
    NumberFeatureDataIndices.u 
  EndStructure
  
  Structure _HIDP_CAPS_Range Align #PB_Structure_AlignC
    UsageMin.w
    UsageMax.w
    StringMin.u
    StringMax.u
    DesignatorMin.u
    DesignatorMax.u
    DataIndexMin.u
    DataIndexMax.u
  EndStructure
  Structure _HIDP_CAPS_NotRange Align #PB_Structure_AlignC
    Reserved1.w
    Usage.w
    StringIndex.u
    Reserved2.u
    DesignatorIndex.u
    Reserved3.u
    DataIndex.u
    Reserved4.u
  EndStructure
  Structure HIDP_BUTTON_CAPS Align #PB_Structure_AlignC
    UsagePage.w;
    ReportID.a ;
    IsAlias.b  ;
    BitField.u ;
    LinkCollection.u;
    LinkUsage.w     ;
    LinkUsagePage.w ;
    IsRange.b       ;
    IsStringRange.b ;
    IsDesignatorRange.b;
    IsAbsolute.b       ;
    Reserved.l[10]     ;
    StructureUnion
      Range._HIDP_CAPS_Range
      NotRange._HIDP_CAPS_NotRange
    EndStructureUnion
  EndStructure
  Structure HIDP_VALUE_CAPS Align #PB_Structure_AlignC
    UsagePage.w;
    ReportID.a ;
    IsAlias.b  ;
    BitField.u ;
    LinkCollection.u;
    LinkUsage.w     ;
    LinkUsagePage.w ;
    IsRange.b       ;
    IsStringRange.b ;
    IsDesignatorRange.b;
    IsAbsolute.b       ;
    HasNull.b          ;
    Reserved.a         ;
    BitSize.u          
    ReportCount.u      ;
    Reserved2.u[5]     ;
    UnitsExp.l         ;
    Units.l            ;
    LogicalMin.l       
    LogicalMax.l       ;
    PhysicalMin.l      ;
    PhysicalMax.l      ;
    StructureUnion
      Range._HIDP_CAPS_Range
      NotRange._HIDP_CAPS_NotRange
    EndStructureUnion
  EndStructure
  ;}

  ;{ Constants
  #RIDI_DEVICENAME=$20000007
  #RIDI_DEVICEINFO=$2000000b
  #RIDI_PREPARSEDDATA=$20000005
  #RIM_TYPEHID=2
  #RIM_TYPEKEYBOARD=1
  #RIM_TYPEMOUSE=0
  #RID_HEADER=$10000005
  #RID_INPUT=$10000003
  #RIDEV_INPUTSINK=$00000100
  
  #HIDP_STATUS_SUCCESS=$110000
  
  Enumeration HIDP_REPORT_TYPE
    #HidP_Input
    #HidP_Output
    #HidP_Feature
  EndEnumeration
  ;}
  
  Procedure init()
    If DLLuser32<>0
      ProcedureReturn #False
    EndIf
    DLLuser32=OpenLibrary(#PB_Any,"user32.dll")
    DLLhid=OpenLibrary(#PB_Any,"hid.dll")
    GetRawInputDeviceList= GetFunction(DLLuser32, "GetRawInputDeviceList")
    GetRawInputDeviceInfo = GetFunction(DLLuser32, "GetRawInputDeviceInfoW")
    RegisterRawInputDevices = GetFunction(DLLuser32, "RegisterRawInputDevices")
    GetRawInputData = GetFunction(DLLuser32, "GetRawInputData")
    
    HidP_GetCaps = GetFunction(DLLhid, "HidP_GetCaps")
    HidP_GetButtonCaps = GetFunction(DLLhid, "HidP_GetButtonCaps")
    HidP_GetValueCaps = GetFunction(DLLhid, "HidP_GetValueCaps") 
    HidP_GetUsages = GetFunction(DLLhid, "HidP_GetUsages")
    HidP_GetUsageValue = GetFunction(DLLhid, "HidP_GetUsageValue")
    HidP_InitializeReportForID = GetFunction(DLLhid, "HidP_InitializeReportForID")
    
    HidD_GetProductString = GetFunction(DLLhid, "HidD_GetProductString")
    HidD_GetPreparsedData = GetFunction(DllHid, "HidD_GetPreparsedData")
    HidD_FreePreparsedData = GetFunction(DLLhid, "HidD_FreePreparsedData")
    HidD_GetInputReport = GetFunction(DLLhid, "HidD_GetInputReport")
    *buf256=AllocateMemory(#Buf256Size)
    ProcedureReturn scan()
  EndProcedure
  
  Procedure exit()
    If DLLuser32=0
      ProcedureReturn #False
    EndIf
    CloseLibrary(DLLuser32)
    CloseLibrary(DLLhid)
    DLLuser32=0
    DLLhid=0
    GetRawInputDeviceList= 0
    GetRawInputDeviceInfo = 0
    RegisterRawInputDevices = 0
    GetRawInputData = 0 
    
    HidP_GetCaps = 0
    HidP_GetButtonCaps = 0
    HidP_GetValueCaps = 0 
    HidP_GetUsages = 0
    HidP_GetUsageValue = 0
    HidP_GetScaledUsageValue = 0
    HidP_InitializeReportForID = 0
    
    HidD_GetProductString = 0
    HidD_GetPreparsedData = 0
    HidD_FreePreparsedData = 0
    HidD_GetInputReport = 0
    FreeMemory(*buf256)
    *buf256=0
  EndProcedure  
  
  Procedure EnableRemapPS4(flag)
    PS4Remap=flag
  EndProcedure
    
  Structure _sJoysticks_caps
    InputReportByteLength.u
    NumberInputButtonCaps.u
    NumberInputValueCaps.u    
  EndStructure
  Structure _sJoysticks_Hid
    hDevice.i
    DevicePath.s
    Usage.u
    UsagePage.u
  EndStructure
  
  Structure sJoysticks
    Hid._sJoysticks_Hid
    Caps._sJoysticks_caps
    Joystick.sJoystick    
  EndStructure
  
  
  
  Global NewList Joysticks.sJoysticks()
  
  Procedure ClearJoysticks()
    ClearList( joysticks() )
  EndProcedure
  
  Procedure Enumerate(List handle.i())
    ClearList(handle())
    ForEach joysticks()
      AddElement(handle())
      handle()=Joysticks()\Hid\hDevice
    Next
    ProcedureReturn ListSize(joysticks())
  EndProcedure
  
  Procedure scan()
    Protected size.u,count.u,i,hidHandle
    Protected DeviceInfo.RID_DEVICE_INFO
    Protected caps.HIDP_CAPS
    Protected PD.i,*ButtonCaps.HIDP_BUTTON_CAPS,capsLength.u,pos
          
    ClearJoysticks()
    
    If GetRawInputDeviceList(#Null,@count,SizeOf (RAWINPUTDEVICELIST) )>#HID_Error Or count=0
      ProcedureReturn #False
    EndIf
    
    Dim Devices.RAWINPUTDEVICELIST(count-1)
    
    If GetRawInputDeviceList(@devices(),@count,SizeOf(RAWINPUTDEVICELIST))>#HID_Error
      ProcedureReturn #False
    EndIf

    For pos=0 To count-1

      If Devices(pos)\dwType=#RIM_TYPEHID
        size.u=SizeOf(rid_device_info)  
        If GetRawInputDeviceInfo(devices(pos)\hDevice,#RIDI_DEVICEINFO,@DeviceInfo,@size)>#HID_Error
          ClearStructure(DeviceInfo,RID_DEVICE_INFO)
        EndIf
         
        If DeviceInfo\hid\usUsagePage=$1 And (DeviceInfo\hid\usUsage=4 Or DeviceInfo\hid\usUsage=5)
          AddElement(joysticks())
          joysticks()\hid\hDevice   = devices(pos)\hDevice
          joysticks()\Joystick\HID\VendorId  = DeviceInfo\hid\dwVendorId
          joysticks()\Joystick\hid\ProductId = DeviceInfo\hid\dwProductId
          joysticks()\hid\Usage     = DeviceInfo\hid\usUsage
          joysticks()\hid\UsagePage = DeviceInfo\hid\usUsagePage
       
          ;Device Path
          size.u=#Buf256Size
          If GetRawInputDeviceInfo(devices(pos)\hDevice,#RIDI_DEVICENAME,*buf256,@size)<#HID_Error
            joysticks()\hid\DevicePath=PeekS(*buf256)
          
            hidHandle=CreateFile_(*buf256,#GENERIC_READ|#GENERIC_WRITE,#FILE_SHARE_READ|#FILE_SHARE_WRITE,#Null,#OPEN_EXISTING,#Null,#Null)
            If hidHandle
              If HidD_GetProductString(hidHandle,*buf256,#Buf256Size)
                joysticks()\Joystick\Name=PeekS(*buf256)
              EndIf
              
              If HidD_GetPreparsedData(hidHandle,@pd)
                If HidP_GetCaps(pd, @caps)=#HIDP_STATUS_SUCCESS
                  joysticks()\Caps\InputReportByteLength = caps\InputReportByteLength
                  joysticks()\Caps\NumberInputButtonCaps = caps\NumberInputButtonCaps
                  joysticks()\Caps\NumberInputValueCaps  = caps\NumberInputValueCaps
                EndIf
                
                ;Number of Buttons
                *ButtonCaps=AllocateMemory(SizeOf(HIDP_BUTTON_CAPS) * caps\NumberInputButtonCaps)
                capsLength.u = Caps\NumberInputButtonCaps
                If HidP_GetButtonCaps(#HidP_Input, *ButtonCaps, @capsLength,pd) = #HIDP_STATUS_SUCCESS
                  If *ButtonCaps\IsRange And *ButtonCaps\IsAbsolute And *ButtonCaps\UsagePage=$09
                    Joysticks()\Joystick\Button\Number = *ButtonCaps\Range\UsageMax - *ButtonCaps\range\UsageMin+1
                  EndIf
                EndIf
                FreeMemory(*ButtonCaps)
                
                ;Axis
                Dim ValueCaps.HIDP_VALUE_CAPS(caps\NumberInputValueCaps-1)
                
                capsLength.u = caps\NumberInputValueCaps
                If HidP_GetValueCaps( #HidP_Input,@ValueCaps(), @capsLength,pd) = #HIDP_STATUS_SUCCESS
                  For i=0 To caps\NumberInputValueCaps-1
                    If ValueCaps(i)\IsAbsolute And ValueCaps(i)\IsRange=#False And ValueCaps(i)\UsagePage=1 And (ValueCaps(i)\NotRange\Usage=>$30 And ValueCaps(i)\NotRange\Usage<=$39)
                      If ValueCaps(i)\NotRange\Usage=$39
                        joysticks()\Joystick\DPad\exist=#True
                      Else
                       
                        If joysticks()\Joystick\HIDid=#HIDID_PS4
                          PS4AxisRemap(ValueCaps(i)\NotRange\Usage)
                        EndIf
                        
                        joysticks()\Joystick\AxisNb[ ValueCaps(i)\NotRange\Usage -$30 ]\exist=#True
                        
                      EndIf
                    EndIf                    
                  Next                  
                EndIf                
                HidD_FreePreparsedData(pd)
                pd=0
              EndIf
              
              CloseHandle_(hidHandle)
            EndIf
            
          EndIf
          
        EndIf          
      EndIf
    Next
    
    
    Protected json
    json=CreateJSON(#PB_Any)
    If json
      InsertJSONList(JSONValue(json), joysticks())
      Debug ComposeJSON(json, #PB_JSON_PrettyPrint)
      FreeJSON(json)
    EndIf
  
    
    ProcedureReturn ListSize(joysticks())
  EndProcedure
  
  Procedure Register(WindowNb,without_focus=#False)
    Dim rid.RAWINPUTDEVICE(1)
    
    rid(0)\usUsagePage=1
    rid(0)\usUsage=5
    rid(0)\hwndTarget=WindowID(WindowNb)
    rid(0)\dwFlags=#RIDEV_DEVNOTIFY
    rid(1)\usUsagePage=1
    rid(1)\usUsage=4
    rid(1)\hwndTarget=WindowID(WindowNb)
    rid(1)\dwFlags=#RIDEV_DEVNOTIFY
    If without_focus
      rid(0)\dwFlags | #RIDEV_INPUTSINK
      rid(1)\dwFlags | #RIDEV_INPUTSINK
    EndIf
    ProcedureReturn registerrawinputdevices(rid(),2,SizeOf(RAWINPUTDEVICE))
  EndProcedure
  
  Macro _FreeWMInput()
    If *rawinput
      FreeMemory(*rawinput):*rawinput=0
    EndIf
    If *PreparseData
      FreeMemory(*PreparseData):*PreparseData=0
    EndIf         
  EndMacro
  
  Procedure Parse_WM_Input()
    Protected size.u,ret
    Protected hDevice,*report,reportLen
    Protected *Rawinput.RAWINPUT,*PreparseData,buffersize.u
    
    ;Get size
    If GetRawInputData(EventlParam(),#RID_INPUT,#Null,@size,SizeOf(RAWINPUTHEADER))<>0
      ProcedureReturn #False
    EndIf
    
    ;Allocate Memory
    *Rawinput=AllocateMemory(size)
    If *Rawinput=0
      _FreeWMInput()
      ProcedureReturn #False
    EndIf
    
    ;Get *RawInput
    If GetRawInputData(EventlParam(),#RID_INPUT,*Rawinput,@size,SizeOf(RAWINPUTHEADER))<>size
      _FreeWMInput()
      ProcedureReturn #False
    EndIf
        
    hDevice=*Rawinput\header\hDevice
    *report = @ *Rawinput\hid\bRawData
    reportLen = *Rawinput\hid\dwSizeHid
    
    Protected ok
    ;Find Joystick
    ForEach Joysticks()
      If  hDevice= Joysticks()\hid\hDevice
        ok=#True
        Break
      EndIf
    Next
    
    If ok=#False
      _FreeWMInput()
      ProcedureReturn #False
    EndIf    
    
    ;Get BUffersize
    If GetRawInputDeviceInfo( hDevice, #RIDI_PREPARSEDDATA ,#Null, @buffersize)<>0
      _FreeWMInput()
      ProcedureReturn #False
    EndIf
    
    ;Allocate
    *PreparseData=AllocateMemory(buffersize)
    If *PreparseData=0
      _FreeWMInput()
      ProcedureReturn #False
    EndIf
    
    ;Get PreparseData
    If GetRawInputDeviceInfo( hDevice, #RIDI_PREPARSEDDATA ,*PreparseData,@buffersize)<>buffersize
      _FreeWMInput()
      ProcedureReturn #False
    EndIf
    
    
    ret=Parse(*PreparseData,hDevice,*report,reportLen)
    
    _FreeWMInput()
    ProcedureReturn ret    
  EndProcedure
    
  Procedure GetReport(HidDeviceObject.i, Type.i, id.a, *report.ascii, reportLen.l, *PreparseData)
    Debug ""+HidDeviceObject+" "+type+" "+id+" "+ *report +" "+reportLen+" "+*PreparseData
    
    If HidP_InitializeReportForID(type,id,*PreparseData,*report,reportLen) <> #HIDP_STATUS_SUCCESS
      Debug "inital-error"
      ProcedureReturn #False
    EndIf
    
    If HidD_GetInputReport(HidDeviceObject,*report,reportLen-1) = 0
      Debug "input-Report-error"
      ProcedureReturn #False
    EndIf
    
    ProcedureReturn #True
  EndProcedure
  
  Procedure Examine(hDevice)
    Protected ok,hidHandle,pd,*ret,*report
    ;Find Joystick
    ForEach Joysticks()
      If  hDevice= Joysticks()\hid\hDevice
        ok=#True
        Break
      EndIf
    Next
    
    If ok=#False
      ProcedureReturn #False
    EndIf
    
   
    *report=AllocateMemory(joysticks()\Caps\InputReportByteLength+2)
    If *report
      hidHandle=CreateFile_(joysticks()\Hid\DevicePath,#GENERIC_READ|#GENERIC_WRITE,#FILE_SHARE_READ|#FILE_SHARE_WRITE,#Null,#OPEN_EXISTING,#FILE_FLAG_OVERLAPPED,#Null)
      If hidHandle=#INVALID_HANDLE_VALUE
        hidHandle=0
      EndIf
      Debug "HidHandle:"+hidHandle
      If hidHandle
        Debug "ok"
        If HidD_GetPreparsedData(hidHandle,@pd)
          Protected hidcaps.HIDP_CAPS
          If parse(pd,hDevice,*report,joysticks()\Caps\InputReportByteLength,hidHandle)
            *ret=joysticks()\Joystick
          EndIf
          HidD_FreePreparsedData(pd)
          pd=0
        EndIf
        
        CloseHandle_(hidHandle)
      EndIf
      
      FreeMemory(*report)
    EndIf
      
    ProcedureReturn *ret
  EndProcedure
  
  Macro _freeParse()
    
  EndMacro
  Procedure Parse(*PreparseData,hDevice,*report,reportLen,hidhandle=0); Joysticks() must be set!
    Protected capsLength.u,usageLength.l,i,value.l,bitmask.l,half.i
    
    ; Check Buttons
    
    ;Get ButtonCaps
    capsLength.u = joysticks()\Caps\NumberInputButtonCaps
    Dim ButtonCaps.HIDP_BUTTON_CAPS( capsLength-1 )
    If HidP_GetButtonCaps(#HidP_Input, @ButtonCaps(0), @capsLength,*PreparseData) <> #HIDP_STATUS_SUCCESS
      ProcedureReturn #False
    EndIf
    
    
    
    ;Get Buttons
    If ButtonCaps(0)\IsRange And ButtonCaps(0)\IsAbsolute And ButtonCaps(0)\UsagePage=$09
      
      If hidhandle And GetReport(hidhandle,#HidP_Input,buttoncaps(0)\ReportID,*report,reportLen,*PreparseData)=#False
        Debug "GetReportError"
        ;ProcedureReturn #False
      EndIf
      
      usageLength.l=ButtonCaps(0)\Range\UsageMax - ButtonCaps(0)\range\UsageMin+1
      Dim usage.u(usageLength)
      joysticks()\Joystick\Button\Value=0
      If HidP_GetUsages( #HidP_Input, ButtonCaps(0)\UsagePage, #Null, @usage(), @usageLength, *PreparseData, *report, reportLen) = #HIDP_STATUS_SUCCESS
        For i=0 To usageLength-1
          usage(i) - ButtonCaps(0)\Range\UsageMin
          If joysticks()\Joystick\HIDid=#HIDID_PS4
            PS4ButtonRemap(usage(i))
          EndIf
          If usage(i)<32
            joysticks()\Joystick\Button\Value | (1 << usage(i))
          EndIf
        Next
      EndIf
    EndIf
    
    
    ;Get ValueCaps
    capsLength.u = joysticks()\caps\NumberInputValueCaps
    Dim ValueCaps.HIDP_VALUE_CAPS( capsLength-1 )
    If HidP_GetValueCaps( #HidP_Input, @ValueCaps(0), @capsLength,*PreparseData) <> #HIDP_STATUS_SUCCESS
      ProcedureReturn #False
    EndIf
    
    ;Get Values
    For i=0 To joysticks()\Caps\NumberInputValueCaps-1
      If ValueCaps(i)\IsAbsolute And ValueCaps(i)\IsRange=#False And ValueCaps(i)\UsagePage=1 And (ValueCaps(i)\NotRange\Usage=>$30 And ValueCaps(i)\NotRange\Usage<=$39)
        
        If hidhandle And GetReport(hidhandle,#HidP_Input,ValueCaps(i)\ReportID,*report,reportLen,*PreparseData)=#False
          ;ProcedureReturn #False
        EndIf        
        
        If HidP_GetUsageValue( #HidP_Input, ValueCaps(i)\UsagePage, #Null, ValueCaps(i)\NotRange\Usage, @value.l,*PreparseData, *report, reportLen)= #HIDP_STATUS_SUCCESS
          
          If ValueCaps(i)\NotRange\Usage=$39
            ;Debug ""+Joysticks()\Joystick\nb+" "+ValueCaps(i)\HasNull+" "+ValueCaps(i)\LogicalMin+" - "+ValueCaps(i)\LogicalMax
            joysticks()\Joystick\DPad\x=0
            joysticks()\Joystick\DPad\y=0
            Select value-ValueCaps(i)\LogicalMin
              Case 7,0,1:joysticks()\Joystick\DPad\y=-1
              Case 5,4,3:joysticks()\Joystick\DPad\y=1
            EndSelect 
            Select value-ValueCaps(i)\LogicalMin
              Case 7,6,5:joysticks()\Joystick\DPad\x=-1
              Case 1,2,3:joysticks()\Joystick\DPad\x=1
            EndSelect 
          Else
            If joysticks()\Joystick\HIDid=#HIDID_PS4
              PS4AxisRemap(ValueCaps(i)\NotRange\Usage)
            EndIf
            
            bitmask= (1 << ValueCaps(i)\BitSize)-1
            ValueCaps(i)\LogicalMin & bitmask
            ValueCaps(i)\LogicalMax & bitmask
            value & bitmask
            value - ValueCaps(i)\LogicalMin
            half = (ValueCaps(i)\LogicalMax- ValueCaps(i)\LogicalMin)/2
            joysticks()\Joystick\AxisNb[ ValueCaps(i)\NotRange\Usage-$30 ]\value=(value-half)*100/half           
            
          EndIf
        EndIf
      EndIf
    Next
    
    ProcedureReturn #True
  EndProcedure
  
  Procedure CheckEvent(WindowEvent)
    Protected ret=0
    
    Select WindowEvent
      Case #WM_INPUT
        If Parse_WM_Input()
          ret=joysticks()\Joystick
        Else
          ret=0
        EndIf
      Case #WM_INPUT_DEVICE_CHANGE
        scan()        
        ret=0
    EndSelect
    
    ProcedureReturn ret    
  EndProcedure
  
  Procedure LastState(handle)
    Protected *ret=#Null
    ForEach joysticks()
      If joysticks()\Hid\hDevice=handle
        *ret=joysticks()\Joystick
        Break
      EndIf
    Next
    ProcedureReturn *ret
  EndProcedure
  
EndModule

CompilerSelect 2;- example 1=rawinput, 2=examine
    CompilerCase 1
  ;Rawinput-version
  
  Procedure FillEditor()
    Protected ii, *joy.joy::sJoystick
    NewList handles()
    joy::Enumerate(handles())
    
    
    a$=""
    ForEach handles()
      *joy=joy::LastState(handles())
      If *joy
        a$+"----------------"+ handles() +#CRLF$
        a$+"Name: "+ *joy\Name +#CRLF$
        a$+"Product: "+Hex( *joy\HIDID) +#CRLF$
        a$+"--"+#CRLF$
        a$+"Buttons:"+*joy\Button\Number + #CRLF$
        a$+"Buttonmap:"+Right("000000000000000000000000000000"+Bin( *joy\Button\Value ),*joy\Button\Number)+" "+*joy\Button\Value+#CRLF$
        For ii=0 To *joy\Button\Number-1
          If (*joy\Button\Value >> ii) &1
            a$+"nr:"+ii+#CRLF$
            
          EndIf
        Next
        
        a$+"--"+#CRLF$
        If *joy\Axis\x\exist
          a$+"X:"+ *joy\axis\x\value+#CRLF$
        EndIf
        If *joy\Axis\y\exist
          a$+"Y:"+ *joy\axis\y\value+#CRLF$
        EndIf
        If *joy\Axis\z\exist
          a$+"Z:"+ *joy\axis\z\value+#CRLF$
        EndIf
        a$+"--"+#CRLF$
        If *joy\Axis\rx\exist
          a$+"rx:"+ *joy\axis\rx\value+#CRLF$
        EndIf
        If *joy\Axis\ry\exist
          a$+"ry:"+ *joy\axis\ry\value+#CRLF$
        EndIf
        If *joy\Axis\rz\exist
          a$+"rZ:"+ *joy\axis\rz\value+#CRLF$
        EndIf
        a$+"--"+#CRLF$
        If *joy\Axis\Slider\exist
          a$+"Slider:"+ *joy\axis\Slider\value+#CRLF$
        EndIf
        a$+"--"+#CRLF$
        If *joy\DPad\exist
          a$+"DigiPad X:"+ *joy\DPad\x +#CRLF$
          a$+"DigiPad y:"+ *joy\DPad\y +#CRLF$
        EndIf
      EndIf
    Next
    If GetGadgetText(0)<> a$
      SetGadgetText(0,a$)
    EndIf
  EndProcedure

  count=joy::init()
  Debug count
  
  joy::EnableRemapPS4(#True)
  
  If OpenWindow(0, 100, 200, 800, 600, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
    joy::Register(0,#True)
    
    EditorGadget(0,0,0,WindowWidth(0),WindowHeight(0))
    
    FillEditor()
    
    Repeat
      Event = WaitWindowEvent()
      Select event
          
          
        Case #PB_Event_CloseWindow  ; If the user has pressed on the close button
          Quit = 1
        Default
          If joy::CheckEvent(event)<> -1
            FillEditor()
          EndIf
          
      EndSelect
      
      
    Until Quit = 1
    
  EndIf
CompilerCase  2
  ;examine-version
  
  Procedure FillEditor()
    Protected ii, *joy.joy::sJoystick
    NewList handles()
    joy::Enumerate(handles())
        
    a$="Start"+#CRLF$
    ForEach handles()
      *joy=joy::Examine(handles())
      If *joy
        a$+"----------------"+ handles() +#CRLF$
        a$+"Name: "+ *joy\Name +#CRLF$
        a$+"Product: "+Hex( *joy\HIDID) +#CRLF$
        a$+"--"+#CRLF$
        a$+"Buttons:"+*joy\Button\Number + #CRLF$
        a$+"Buttonmap:"+Right("000000000000000000000000000000"+Bin( *joy\Button\Value ),*joy\Button\Number)+" "+*joy\Button\Value+#CRLF$
        For ii=0 To *joy\Button\Number-1
          If (*joy\Button\Value >> ii) &1
            a$+"nr:"+ii+#CRLF$
            
          EndIf
        Next
        
        a$+"--"+#CRLF$
        If *joy\Axis\x\exist
          a$+"X:"+ *joy\axis\x\value+#CRLF$
        EndIf
        If *joy\Axis\y\exist
          a$+"Y:"+ *joy\axis\y\value+#CRLF$
        EndIf
        If *joy\Axis\z\exist
          a$+"Z:"+ *joy\axis\z\value+#CRLF$
        EndIf
        a$+"--"+#CRLF$
        If *joy\Axis\rx\exist
          a$+"rx:"+ *joy\axis\rx\value+#CRLF$
        EndIf
        If *joy\Axis\ry\exist
          a$+"ry:"+ *joy\axis\ry\value+#CRLF$
        EndIf
        If *joy\Axis\rz\exist
          a$+"rZ:"+ *joy\axis\rz\value+#CRLF$
        EndIf
        a$+"--"+#CRLF$
        If *joy\Axis\Slider\exist
          a$+"Slider:"+ *joy\axis\Slider\value+#CRLF$
        EndIf
        a$+"--"+#CRLF$
        If *joy\DPad\exist
          a$+"DigiPad X:"+ *joy\DPad\x +#CRLF$
          a$+"DigiPad y:"+ *joy\DPad\y +#CRLF$
        EndIf
      EndIf
    Next
    If GetGadgetText(0)<> a$
      SetGadgetText(0,a$)
    EndIf
  EndProcedure
  
  count=joy::init()
  Debug count
  
  joy::EnableRemapPS4(#True)
  
  If OpenWindow(0, 100, 200, 800, 600, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
    
    EditorGadget(0,0,0,WindowWidth(0),WindowHeight(0))
    AddWindowTimer(0,1,1000/30);30fps
    FillEditor()
    
    Repeat
      Event = WaitWindowEvent()
      Select event
        Case #PB_Event_Timer
          FillEditor()
          
        Case #PB_Event_CloseWindow  ; If the user has pressed on the close button
          Quit = 1
        Default
          If joy::CheckEvent(event)<> -1
            FillEditor()
          EndIf
          
      EndSelect
      
      
    Until Quit = 1
    
  EndIf
CompilerEndSelect