{******************************************************************

                       JEDI-VCL Demo

 Copyright (C) 2002 Project JEDI

 Original author:

 Contributor(s):

 You may retrieve the latest version of this file at the JEDI-JVCL
 home page, located at http://jvcl.delphi-jedi.org

 The contents of this file are used with permission, subject to
 the Mozilla Public License Version 1.1 (the "License"); you may
 not use this file except in compliance with the License. You may
 obtain a copy of the License at
 http://www.mozilla.org/MPL/MPL-1_1Final.html

 Software distributed under the License is distributed on an
 "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 implied. See the License for the specific language governing
 rights and limitations under the License.

******************************************************************}

unit CollectionMain;

interface

uses
  Windows, SysUtils, Classes, Controls, Forms, ComCtrls,
  JvHidControllerClass, Dialogs, StdCtrls;

type
  TCollectionDemoForm = class(TForm)
    HidCtl: TJvHidDeviceController;
    DeviceTree: TTreeView;
    Save: TButton;
    SaveDialog: TSaveDialog;
    Description: TLabel;
    procedure FormShow(Sender: TObject);
    procedure HidCtlDeviceChange(Sender: TObject);
    function  HidCtlEnumerate(HidDev: TJvHidDevice;
      Index: Integer): Boolean;
    procedure SaveClick(Sender: TObject);
  public
    Root: TTreeNode;
    procedure EnumerateCaps(HidDev: TJvHidDevice;
      Parent: TTreeNode; Idx: Word);
    procedure EnumerateNodes(HidDev: TJvHidDevice;
      Parent: TTreeNode; Idx: Word; NumSiblings: Word);
  end;

var
  CollectionDemoForm: TCollectionDemoForm;

implementation

uses
  UsagesInfo, Hid;

{$R *.DFM}

procedure TCollectionDemoForm.FormShow(Sender: TObject);
begin
  // this compensates the possibility that HidCtlDeviceChange
  // may not be able to fill the treeview the first time
  HidCtlDeviceChange(Self);
end;

procedure TCollectionDemoForm.HidCtlDeviceChange(Sender: TObject);
begin
  // HidCtlDeviceChange happens before TCollectionDemoForm.Create completed
  // DeviceTree may have not been created yet
  if Assigned(DeviceTree) then
  begin
    // each time something happens to the HID devices the treeview is filled
    DeviceTree.Items.Clear;
    Root := DeviceTree.Items.Add(nil, 'HID-Devices');
    HidCtl.Enumerate;
  end;
end;

procedure TCollectionDemoForm.EnumerateCaps(HidDev: TJvHidDevice; Parent: TTreeNode; Idx: Word);

  procedure EnumerateButtonCaps(HidDev: TJvHidDevice; Parent: TTreeNode;
    ReportType: THIDPReportType; ReportText: string);
  var
    I: Integer;
    Ret: Integer;
    N: WORD;
    UsagePageText: string;
    UsageText: string;
    // 256 Caps entries should be always sufficient
    BtnCaps: array [0..255] of THIDPButtonCaps;
  begin
    N := 255;
    // get ALL ButtonCaps of the specified ReportType
    HidDev.ReportTypeParam := ReportType;
    Ret := HidDev.GetSpecificButtonCaps(@BtnCaps[0], N);
    if Ret = HIDP_STATUS_SUCCESS then
      for I := 0 to N-1 do
        if BtnCaps[I].LinkCollection = Idx then
        begin
          UsageAndUsagePageText(BtnCaps[I].UsagePage, BtnCaps[I].Usage, UsagePageText, UsageText);
          if BtnCaps[I].IsRange then
            DeviceTree.Items.AddChild(Parent,
              Format('Button %s Range: UsagePage=%s ($%.4x) %d..%d',
                [ReportText, UsagePageText, BtnCaps[I].UsagePage, BtnCaps[I].UsageMin, BtnCaps[I].UsageMax]))
          else
            DeviceTree.Items.AddChild(Parent,
              Format('Button %s: UsagePage=%s ($%.4x) Usage=%s ($%.4x)',
                [ReportText, UsagePageText, BtnCaps[I].UsagePage, UsageText, BtnCaps[I].Usage]));
        end;
  end;

  procedure EnumerateValueCaps(HidDev: TJvHidDevice; Parent: TTreeNode;
    ReportType: THIDPReportType; ReportText: string);
  var
    I: Integer;
    Ret: Integer;
    N: WORD;
    UsagePageText: string;
    UsageText: string;
    // 256 Caps entries should be always sufficient
    ValCaps: array [0..255] of THIDPValueCaps;
  begin
    N := 255;
    // get ALL ValueCaps of the specified ReportType
    HidDev.ReportTypeParam := ReportType;
    Ret := HidDev.GetSpecificValueCaps(@ValCaps[0], N);
    if Ret = HIDP_STATUS_SUCCESS then
      for I := 0 to N-1 do
        if ValCaps[I].LinkCollection = Idx then
        begin
          UsageAndUsagePageText(ValCaps[I].UsagePage, ValCaps[I].Usage, UsagePageText, UsageText);
          if ValCaps[I].IsRange then
            DeviceTree.Items.AddChild(Parent,
              Format('Value %s Range: UsagePage=%s ($%.4x) %d..%d',
                [ReportText, UsagePageText, ValCaps[I].UsagePage, ValCaps[I].UsageMin, ValCaps[I].UsageMax]))
          else
            DeviceTree.Items.AddChild(Parent,
              Format('Value %s: UsagePage=%s ($%.4x) Usage=%s ($%.4x)',
                [ReportText, UsagePageText, ValCaps[I].UsagePage, UsageText, ValCaps[I].Usage]));
        end;
  end;

begin
  // set the params to get ALL Caps
  HidDev.UsagePageParam      := 0;
  HidDev.UsageParam          := 0;
  HidDev.LinkCollectionParam := HIDP_LINK_COLLECTION_UNSPECIFIED;
  EnumerateButtonCaps(HidDev, Parent, HidP_Input,   'Input');
  EnumerateButtonCaps(HidDev, Parent, HidP_Output,  'Output');
  EnumerateButtonCaps(HidDev, Parent, HidP_Feature, 'Feature');
  EnumerateValueCaps (HidDev, Parent, HidP_Input,   'Input');
  EnumerateValueCaps (HidDev, Parent, HidP_Output,  'Output');
  EnumerateValueCaps (HidDev, Parent, HidP_Feature, 'Feature');
end;

procedure TCollectionDemoForm.EnumerateNodes(HidDev: TJvHidDevice;
  Parent: TTreeNode; Idx: Word; NumSiblings: Word);
var
  I: Word;
  Node: TTreeNode;
  UsagePageText: string;
  UsageText: string;
  CollectionTypeText: string;
  NodeText: string;
begin
  // add a list of sibling nodes to the device tree node Parent
  for I := 1 to NumSiblings do
  begin
    UsageAndUsagePageText(HidDev.LinkCollectionNodes[Idx].LinkUsagePage,
      HidDev.LinkCollectionNodes[Idx].LinkUsage, UsagePageText, UsageText);
    case HidDev.LinkCollectionNodes[Idx].CollectionType of
      $00:
        CollectionTypeText := 'Physical';
      $01:
        CollectionTypeText := 'Application';
      $02:
        CollectionTypeText := 'Logical';
      $03:
        CollectionTypeText := 'Report';
      $04:
        CollectionTypeText := 'Named Array';
      $05:
        CollectionTypeText := 'Usage Switch';
      $06:
        CollectionTypeText := 'Usage Modifier';
      $07..$7F:
        CollectionTypeText := Format('Reserved $%.2x',
          [Cardinal(HidDev.LinkCollectionNodes[Idx].CollectionType)]);
      $80..$FF:
        CollectionTypeText := Format('Vendor-defined $%.2x',
          [Cardinal(HidDev.LinkCollectionNodes[Idx].CollectionType)]);
    end;

    NodeText :=
      'UsagePage='      + UsagePageText      + Format(' ($%.4x)  ', [HidDev.LinkCollectionNodes[Idx].LinkUsagePage]) +
      'Usage='          + UsageText          + Format(' ($%.4x)  ', [HidDev.LinkCollectionNodes[Idx].LinkUsage]) +
      'CollectionType=' + CollectionTypeText + Format(' ($%.2x)',   [HidDev.LinkCollectionNodes[Idx].CollectionType]);
    if HidDev.LinkCollectionNodes[Idx].IsAlias <> 0 then
      NodeText := NodeText + '  IsAlias';
    Node := DeviceTree.Items.AddChild(Parent, NodeText);

    EnumerateCaps(HidDev, Node, Idx);

    // recurse to the children nodes
    if HidDev.LinkCollectionNodes[Idx].FirstChild <> 0 then
      EnumerateNodes(HidDev, Node,
        HidDev.LinkCollectionNodes[Idx].FirstChild,
        HidDev.LinkCollectionNodes[Idx].NumberOfChildren);
    // secure against buggy descriptors
    if HidDev.LinkCollectionNodes[Idx].NextSibling <> 0 then
      // follow the link to the next sibling
      Idx := HidDev.LinkCollectionNodes[Idx].NextSibling
    else
      Break;
  end;
end;

function TCollectionDemoForm.HidCtlEnumerate(HidDev: TJvHidDevice;
  Index: Integer): Boolean;
var
  Idx: Word;
  Node: TTreeNode;
  Name: string;
begin
  // give the treeview node for the device a descriptive name
  if HidDev.ProductName <> '' then
    Name := Format('"%s"  VID=$%.4x PID=$%.4x',
      [HidDev.ProductName, HidDev.Attributes.VendorID, HidDev.Attributes.ProductID])
  else
    Name := Format('VID=$%.4x PID=$%.4x',
      [HidDev.Attributes.VendorID, HidDev.Attributes.ProductID]);
  Node := DeviceTree.Items.AddChild(Root, Name);

  // there is only one root node, but we try to enumerate siblings anyway
  // in case there is a bad descriptor
  Idx := 0;
  repeat
    EnumerateNodes(HidDev, Node, Idx, 1);
    Idx := HidDev.LinkCollectionNodes[Idx].NextSibling;
  until Idx = 0;

  DeviceTree.FullExpand;
  Result := True;
end;

procedure TCollectionDemoForm.SaveClick(Sender: TObject);
begin
  // write the treeview content to a file to avoid the need
  // to send screenshots
  if SaveDialog.Execute then
    DeviceTree.SaveToFile(SaveDialog.FileName);
end;

end.