unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, nrclasses, nrhid, StdCtrls, nrcommbox, ComCtrls, nrhidw32,
  nrlogfile, Spin, XPMan;

type
  TForm1 = class(TForm)
    nrDeviceBox1: TnrDeviceBox;
    nrHid1: TnrHid;
    ListView1: TListView;
    CheckBox1: TCheckBox;
    TreeView1: TTreeView;
    Memo1: TMemo;
    Label1: TLabel;
    nrLogFile1: TnrLogFile;
    GroupBox1: TGroupBox;
    eID: TSpinEdit;
    Label2: TLabel;
    Label3: TLabel;
    Edit1: TEdit;
    bSend: TButton;
    cbType: TComboBox;
    Button1: TButton;
    Button2: TButton;
    CheckBox2: TCheckBox;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    XPManifest1: TXPManifest;
    procedure CheckBox1Click(Sender: TObject);
    procedure nrDeviceBox1Change(Sender: TObject);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure nrHid1AfterReceive(Com: TObject; Buffer: Pointer;
      Received: Cardinal);
    procedure bSendClick(Sender: TObject);
    procedure FormDblClick(Sender: TObject);
    procedure cbTypeChange(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
  private
    procedure AddInfo(sCapt, sVal: string);
    { Private declarations }
    procedure UpdateTreeInfo;
    procedure UpdateListViewInfo;
    procedure UpdateGeneralInfo;
    procedure UpdateButtonsInfo(v:HIDP_BUTTON_CAPS);
    procedure UpdateValuesInfo(v:HIDP_VALUE_CAPS);
  public
    { Public declarations }
    procedure WMDeviceChange(var Msg:TMessage); message WM_DEVICECHANGE;      
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.AddInfo(sCapt, sVal: string);
var Itm:TListItem;
begin
  if Trim(sVal) = '' then exit;
  with ListView1 do begin
    Itm := Items.Add;
    Itm.Caption := sCapt;
    Itm.SubItems.Add(sVal);
  end;
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  try
    nrHid1.Active := CheckBox1.Checked;
  except
    raise;
    CheckBox1.Checked := nrHid1.Active;
  end;
end;

procedure TForm1.UpdateListViewInfo;
var
  btns : HIDP_BUTTON_CAPS;
  vals : HIDP_VALUE_CAPS;
begin
  ListView1.Clear;
  if (TreeView1.Selected <> nil) and (nrHid1.HidDevice <> nil) then begin

    if TreeView1.Selected.AbsoluteIndex = 0
      then UpdateGeneralInfo;

    if TreeView1.Selected.Level > 0 then begin
      case Integer(TreeView1.Selected.Parent.Data) of
        0: btns := nrHid1.HidDevice.InputButtons[TreeView1.Selected.Index];
        1: vals := nrHid1.HidDevice.InputValues[TreeView1.Selected.Index];
        2: btns := nrHid1.HidDevice.OutputButtons[TreeView1.Selected.Index];
        3: vals := nrHid1.HidDevice.OutputValues[TreeView1.Selected.Index];
        4: btns := nrHid1.HidDevice.FeatureButtons[TreeView1.Selected.Index];
        5: vals := nrHid1.HidDevice.FeatureValues[TreeView1.Selected.Index];
      end;

      if (Integer(TreeView1.Selected.Parent.Data) and 1) = 0
        then UpdateButtonsInfo(btns)
        else UpdateValuesInfo(vals);
    end;
  end;
end;

procedure TForm1.UpdateTreeInfo;
var i : integer;
    itm : TTreeNode;
begin
  TreeView1.Items.Clear;
  if nrHid1.HidDevice <> nil then begin
    itm := TreeView1.Items.Add(nil,'General HID Info');

    if nrHid1.HidDevice.InputButtonsCount > 0 then begin
      itm := TreeView1.Items.Add(itm,'Input Buttons');
      for i := 0 to nrHid1.HidDevice.InputButtonsCount - 1 do
        TreeView1.Items.AddChild(itm,'Input Button ' + IntToStr(i));
    end;

    if nrHid1.HidDevice.InputValuesCount > 0 then begin
      itm := TreeView1.Items.Add(itm,'Input Values');
      itm.Data := Pointer(1);
      for i := 0 to nrHid1.HidDevice.InputValuesCount - 1 do
        TreeView1.Items.AddChild(itm,'Input Values ' + IntToStr(i));
    end;

    if nrHid1.HidDevice.OutputButtonsCount > 0 then begin
      itm := TreeView1.Items.Add(itm,'Output Buttons');
      itm.Data := Pointer(2);
      for i := 0 to nrHid1.HidDevice.OutputButtonsCount - 1 do
        TreeView1.Items.AddChild(itm,'Output Button ' + IntToStr(i));
    end;

    if nrHid1.HidDevice.OutputValuesCount > 0 then begin
      itm := TreeView1.Items.Add(itm,'Output Values');
      itm.Data := Pointer(3);
      for i := 0 to nrHid1.HidDevice.OutputValuesCount - 1 do
        TreeView1.Items.AddChild(itm,'Output Values ' + IntToStr(i));
    end;

    if nrHid1.HidDevice.FeatureButtonsCount > 0 then begin
      itm := TreeView1.Items.Add(itm,'Feature Buttons');
      itm.Data := Pointer(4);
      for i := 0 to nrHid1.HidDevice.FeatureButtonsCount - 1 do
        TreeView1.Items.AddChild(itm,'Feature Button ' + IntToStr(i));
    end;

    if nrHid1.HidDevice.FeatureValuesCount > 0 then begin
      itm := TreeView1.Items.Add(itm,'Feature Values');
      itm.Data := Pointer(5);
      for i := 0 to nrHid1.HidDevice.FeatureValuesCount - 1 do
        TreeView1.Items.AddChild(itm,'Feature Values ' + IntToStr(i));
    end;

  end;

  if (TreeView1.Items.Count > 0) then TreeView1.Selected := TreeView1.Items[0]; 

  UpdateListViewInfo;
end;

procedure TForm1.UpdateGeneralInfo;
begin
  AddInfo('Friendly Name', nrHid1.HidDevice.NameFriendly);
  AddInfo('Description', nrHid1.HidDevice.Description);
  AddInfo('Manufacturer', nrHid1.HidDevice.Manufacturer);
  AddInfo('S/N', nrHid1.HidDevice.SerialNumber);
  AddInfo('Service', nrHid1.HidDevice.Service);
  AddInfo('VendorID', IntToHex(nrHid1.HidDevice.VendorID, 4));
  AddInfo('ProductID', IntToHex(nrHid1.HidDevice.ProductID, 4));
  AddInfo('Ver', IntToStr(nrHid1.HidDevice.Version) + ' [' + IntToHex(nrHid1.HidDevice.Version, 4) + 'h]');
  AddInfo('Usage Page', IntToHex(nrHid1.HidDevice.UsagePage, 4) + ' (' + GetUsagePageString(nrHid1.HidDevice.UsagePage) + ')');
  AddInfo('I/O Length', IntToStr(nrHid1.HidDevice.InputReportLength) + '/' + IntToStr(nrHid1.HidDevice.OutputReportLength));

  if nrHid1.Active then begin
    AddInfo('R/W modes:', BoolToStr(nrHid1.IsReadMode,True) + ' / ' + BoolToStr(nrHid1.IsWriteMode,True));
  end;
end;

procedure TForm1.nrDeviceBox1Change(Sender: TObject);
begin
  UpdateTreeInfo;
end;

procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
  UpdateListViewInfo;
end;

procedure TForm1.UpdateButtonsInfo(v: HIDP_BUTTON_CAPS);
begin
  AddInfo('Usage Page', IntToHex(v.UsagePage, 4) + ' (' + GetUsagePageString(v.UsagePage) + ')');
  AddInfo('Report ID', IntToHex(v.ReportID, 2));
  AddInfo('Bit Field', IntToHex(v.BitField, 4));
  AddInfo('Is Range', BoolToStr(v.IsRange, True));
  AddInfo('Is Designator Range', BoolToStr(v.IsDesignatorRange, True));
  if v.IsRange then begin
    AddInfo('Usage Min/Max', IntToStr(v.Range.UsageMin) + ' / ' + IntToStr(v.Range.UsageMax));
    AddInfo('String Min/Max', IntToStr(v.Range.StringMin) + ' / ' + IntToStr(v.Range.StringMax));
    AddInfo('Designator Min/Max', IntToStr(v.Range.DesignatorMin) + ' / ' + IntToStr(v.Range.DesignatorMax));
    AddInfo('Data Index Min/Max', IntToStr(v.Range.DataIndexMin) + ' / ' + IntToStr(v.Range.DataIndexMax));
  end else begin
    AddInfo('Usage', IntToStr(v.NotRange.Usage) + ' [' + IntToHex(v.NotRange.Usage,4) + 'h]');
    AddInfo('String', IntToStr(v.NotRange.StringIndex));
    AddInfo('Designator', IntToStr(v.NotRange.DesignatorIndex));
    AddInfo('Data Index', IntToStr(v.NotRange.DataIndex));
  end;
end;

procedure TForm1.UpdateValuesInfo(v: HIDP_VALUE_CAPS);
begin
  AddInfo('Usage Page', IntToHex(v.UsagePage, 4)  + ' (' + GetUsagePageString(v.UsagePage) + ')' );
  AddInfo('Report ID', IntToHex(v.ReportID, 2));
  AddInfo('Bit Field', IntToHex(v.BitField, 4));
  AddInfo('Is Range', BoolToStr(v.IsRange, True));
  AddInfo('Is Designator Range', BoolToStr(v.IsDesignatorRange, True));
  if v.IsRange then begin
    AddInfo('Usage Min/Max', IntToStr(v.Range.UsageMin) + ' / ' + IntToStr(v.Range.UsageMax));
    AddInfo('String Min/Max', IntToStr(v.Range.StringMin) + ' / ' + IntToStr(v.Range.StringMax));
    AddInfo('Designator Min/Max', IntToStr(v.Range.DesignatorMin) + ' / ' + IntToStr(v.Range.DesignatorMax));
    AddInfo('Data Index Min/Max', IntToStr(v.Range.DataIndexMin) + ' / ' + IntToStr(v.Range.DataIndexMax));
  end else begin
    AddInfo('Usage', IntToStr(v.NotRange.Usage) + ' [' + IntToHex(v.NotRange.Usage,4) + 'h]');
    AddInfo('String', IntToStr(v.NotRange.StringIndex));
    AddInfo('Designator', IntToStr(v.NotRange.DesignatorIndex));
    AddInfo('Data Index', IntToStr(v.NotRange.DataIndex));
  end;
end;

procedure TForm1.nrHid1AfterReceive(Com: TObject; Buffer: Pointer;
  Received: Cardinal);
var s:string;
    i:integer;                        
begin
  s := 'HID Report is received:  ' + IntToStr(Received) + ' bytes ';
  for i := 0 to Received - 1
    do s := s + IntToHex(Byte(PAnsiChar(Buffer)[i]),2) + ' ';
  Memo1.Lines.Add(s);
end;

procedure TForm1.WMDeviceChange(var Msg: TMessage);
var i:integer;
begin
{  try
  i := nrHid1.DeviceIndex;
  nrHid1.WMDeviceChange(Msg);
  if i < nrHid1.DeviceCount then begin
    nrHid1.DeviceIndex := i;
    nrDeviceBox1Change(Self);
  end;
  except
  end;}
end;

function Deserialize(s:string):string;
var i:integer;
begin
  Result := '';
  i := 1;
  while i < Length(s) do begin
    try
      Result := Result + Char(Byte(StrToInt('$' + Copy(s, i, 2))));
    except
      break;
    end;
    Inc(i, 2);
  end;
end;

function Serialize(s:string):string;
var i:integer;
begin
  Result := '';
  for i := 1 to Length(s) do begin
    Result := Result + IntToHex(Byte(s[i]), 2);
  end;
end;

procedure TForm1.bSendClick(Sender: TObject);
var s, s2:string;
    len:integer;
begin
  // prepare data string and reserve first byte for ID ...
  s := Char(eID.Value);
  if cbType.ItemIndex = 0
    then s := s + Edit1.Text
    else s := s + Deserialize(Edit1.Text);

  // send HID report
  if CheckBox2.Checked then begin
    len := Length(s);
    if nrHid1.OutputReportLength < len
      then len := nrHid1.OutputReportLength;
    SetLength(s2,  nrHid1.OutputReportLength);
    CopyMemory(@s2[1], @s[1], len);
    nrHid1.SendString(s2);
  end else
    nrHid1.SendString(s);

  // also the following methods are possible too:
  //
  //
  //nrHid1.WriteReport(@s[1], Length(s));
  //nrHid1.SetOutputReport(@s[1], Length(s));

end;

procedure TForm1.FormDblClick(Sender: TObject);
begin
  ShowMessage(Format('%u %u ',[
  TnrHidDevice(nrHid1.Device[nrHid1.DeviceIndex]).VendorId,
  TnrHidDevice(nrHid1.Device[nrHid1.DeviceIndex]).ProductId]));

end;

procedure TForm1.cbTypeChange(Sender: TObject);
begin
  if cbType.ItemIndex = 0
    then Edit1.Text := Deserialize(Edit1.Text)
    else Edit1.Text :=  Serialize(Edit1.Text);
end;

procedure TForm1.Button1Click(Sender: TObject);
var s, s2:string;
    len:integer;
begin
  // prepare data string and reserve first byte for ID ...
  s := Char(eID.Value);
  if cbType.ItemIndex = 0
    then s := s + Edit1.Text
    else s := s + Deserialize(Edit1.Text);

  // send HID report
  if CheckBox2.Checked then begin
    len := Length(s);
    if nrHid1.OutputReportLength < len
      then len := nrHid1.OutputReportLength;
    SetLength(s2,  nrHid1.OutputReportLength);
    CopyMemory(@s2[1], @s[1], len);
    nrHid1.SendData(@s2[1], nrHid1.OutputReportLength);
  end else
    nrHid1.SendData(@s[1], Length(s));
end;

procedure TForm1.Button2Click(Sender: TObject);
var s,s2:string;
    len:integer;
begin
  // prepare data string and reserve first byte for ID ...
  s := Char(eID.Value);
  if cbType.ItemIndex = 0
    then s := s + Edit1.Text
    else s := s + Deserialize(Edit1.Text);

  // send HID report
  if CheckBox2.Checked then begin
    len := Length(s);
    if nrHid1.OutputReportLength < len
      then len := nrHid1.OutputReportLength;
    SetLength(s2,  nrHid1.OutputReportLength);
    CopyMemory(@s2[1], @s[1], len);
    nrHid1.SetOutputReport(@s2[1], nrHid1.OutputReportLength);
  end else
    nrHid1.SetOutputReport(@s[1], Length(s));

end;

procedure TForm1.FormShow(Sender: TObject);
begin
  nrHid1.Update;             
  UpdateTreeInfo;
end;                            
                           
procedure TForm1.Button3Click(Sender: TObject);
begin
  Screen.Cursor := crHourGlass; 
  nrHid1.UpdateDeviceDetails;
  UpdateTreeInfo;
  Screen.Cursor := crDefault;               
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  nrHid1.Disable;
end;

procedure TForm1.Button5Click(Sender: TObject);
begin
  nrHid1.Enable;
end;

procedure TForm1.Button6Click(Sender: TObject);
begin
  nrHid1.Restart;
end;

end.
