unit frmConfigureUnt;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ExtCtrls, ComCtrls, Grids, ImgList, IntvControls;

type
  TfrmConfigure = class(TForm)
    pcUser: TPageControl;
    btnOk: TBitBtn;
    tbKeyboard: TTabSheet;
    tbMouse: TTabSheet;
    tbDigital: TTabSheet;
    rbMouseXAxis: TRadioButton;
    rbMouseLeft: TRadioButton;
    rbMouseRight: TRadioButton;
    rbMouseMiddle: TRadioButton;
    sgKeyboard: TStringGrid;
    ImageList1: TImageList;
    ImageList2: TImageList;
    ImageList3: TImageList;
    rbMouseYAxis: TRadioButton;
    cbController: TComboBox;
    sgProps: TStringGrid;
    sgController: TStringGrid;
    Timer1: TTimer;
    Label1: TLabel;
    tbExternal: TTabSheet;
    rbLPT1: TRadioButton;
    rbLPT2: TRadioButton;
    rbLPT3: TRadioButton;
    procedure btnOkClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure sgKeyboardDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure sgKeyboardSelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure pcUserChange(Sender: TObject);
    procedure pcControllerChange(Sender: TObject);
    procedure sgKeyboardClick(Sender: TObject);
    procedure rbMouseXAxisClick(Sender: TObject);
    procedure rbMouseYAxisClick(Sender: TObject);
    procedure rbMouseLeftClick(Sender: TObject);
    procedure rbMouseRightClick(Sender: TObject);
    procedure rbMouseMiddleClick(Sender: TObject);
    procedure cbControllerChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure sgPropsDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure sgPropsSelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure sgControllerDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure sgControllerClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Timer1Timer(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure rbLPT1Click(Sender: TObject);
    procedure rbLPT2Click(Sender: TObject);
    procedure rbLPT3Click(Sender: TObject);
  private
    { Private declarations }
    Updating     : Boolean;
    KbdIntf      : TKeyboardInterface;
    MouseIntf    : TMouseInterface;
    DigitalIntf  : TDigitalInterface;
    ExternalIntf : TExternalInterface;
    Cap          : TCapability;
    OldKeys      : Array[0..255] Of Boolean;
    StrWid       : Integer;
    Procedure GetBaseRowCol(Var Row,Col: Integer);
    Procedure LoadControllerInfo;
    Procedure LoadDigitalInfo;
    Function  AxisOnly: Boolean;
    Function  PairOnly: Boolean;
    Procedure SetGridWidths;
    Procedure ChangeCapability;
    Function  CannotChange: Boolean;
    Procedure SetKeyboardSize;
  public
    { Public declarations }
  end;

var
  frmConfigure: TfrmConfigure;

implementation

Uses Base, frmMainunt;

Const
  CellWidth : Array[0..6,0..22] Of Integer =
  ((1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0),
   (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
   (1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1),
   (1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1),
   (1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,0,0,0,0,1,1,1,0),
   (1,1,1,1,1,1,1,1,1,1,1,3,1,2,0,0,1,0,0,1,1,1,1),
   (1,1,1,7,1,2,3,4,5,6,1,1,1,1,0,1,1,1,0,2,1,1,0));

  CellHeight : Array[0..6,0..22] Of Integer =
  ((1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0),
   (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
   (1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1),
   (1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,2),
   (1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1),
   (1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,0,1,1,1,2),
   (1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,1,1,1,0,1,0,1,1));

  CellText : Array[0..6,0..22] Of String =
  (('Esc',  'F1', 'F2', 'F3',   'F4','F5','F6','F7','F8','F9','F10','F11',  'F12',  '',    '','Print','Scrl','Pause','','',   '', '', ''),
   ('',     '',   '',   '',     '',  '',  '',  '',  '',  '',  '',   '',     '',     '',    '','',     '',    '',     '','',   '', '', ''),
   ('`',    '1',  '2',  '3',    '4', '5', '6', '7', '8', '9', '0',  '-',    '=',    'BkSp','','Ins',  'Home','PgUp', '','Num','/','*','-'),
   ('Tab',  'Q',  'W',  'E',    'R', 'T', 'Y', 'U', 'I', 'O', 'P',  '[',    ']',    '\',   '','Del',  'End', 'PgDn', '','7',  '8','9','+'),
   ('Caps', 'A',  'S',  'D',    'F', 'G', 'H', 'J', 'K', 'L', ';',  '''',   'Enter','',    '','',     '',    '',     '','4',  '5','6',''),
   ('Shift','Z',  'X',  'C',    'V', 'B', 'N', 'M', ',', '.', '/',  'Shift','',     '',    '','',     'Up',  '',     '','1',  '2','3','Enter'),
   ('Ctrl', 'Win','Alt','Space','',  '',  '',  '',  '',  '',  'Alt','Win',  'Menu', 'Ctrl','','Lf',   'Dn',  'Rt',   '','0',  '', '.',''));

  CellChangeable : Array[0..6,0..22] Of Boolean =
  ((False,True, True, True, True, True, True,False, True, True, False,True, False,True, True, True, True, True, True, True, True, True, True),
   (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True),
   (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True),
   (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True),
   (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True),
   (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True),
   (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True));

  CellScanCode : Array[0..6,0..22] Of Word =
  (($0001,$003B,$003C,$003D,$003E,$003F,$0040,$0041,$0042,$0043,$0044,$0057,$0058,0,    0,    $00B7,$0046,$0077,0,    0,    0,    0,    0),
   (0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0),
   ($0029,$0002,$0003,$0004,$0005,$0006,$0007,$0008,$0009,$000A,$000B,$000C,$000D,$000E,0,    $00D2,$00C7,$00C9,0,    $0045,$00B5,$0037,$004A),
   ($000F,$0010,$0011,$0012,$0013,$0014,$0015,$0016,$0017,$0018,$0019,$001A,$001B,$002B,0,    $00D3,$00CF,$00D1,0,    $0047,$0048,$0049,$004E),
   ($003A,$001E,$001F,$0020,$0021,$0022,$0023,$0024,$0025,$0026,$0027,$0028,$001C,0,    0,    0,    0,    0,    0,    $004B,$004C,$004D,0),
   ($002A,$002C,$002D,$002E,$002F,$0030,$0031,$0032,$0033,$0034,$0035,$0036,0,    0,    0,    0,    $00C8,0,    0,    $004F,$0050,$0051,$001C),
   ($001D,$00DB,$0038,$0039,0,    0,    0,    0,    0,    0,    $00B8,$00DC,$00DD,$009D,0,    $00CB,$00D0,$00CD,0,    $0052,0,    $0053,0));

  PropsCells : Array[0..31,0..4] Of String =
  (('Console switches', 'Controller 1', 'Controller 2', 'Controller 3', 'Controller 4'),
   ('Power',            'Keypad 1',     'Keypad 1',     'Keypad 1',     'Keypad 1'),
   ('Reset',            'Keypad 2',     'Keypad 2',     'Keypad 2',     'Keypad 2'),
   ('',                 'Keypad 3',     'Keypad 3',     'Keypad 3',     'Keypad 3'),
   ('',                 'Keypad 4',     'Keypad 4',     'Keypad 4',     'Keypad 4'),
   ('',                 'Keypad 5',     'Keypad 5',     'Keypad 5',     'Keypad 5'),
   ('',                 'Keypad 6',     'Keypad 6',     'Keypad 6',     'Keypad 6'),
   ('',                 'Keypad 7',     'Keypad 7',     'Keypad 7',     'Keypad 7'),
   ('',                 'Keypad 8',     'Keypad 8',     'Keypad 8',     'Keypad 8'),
   ('',                 'Keypad 9',     'Keypad 9',     'Keypad 9',     'Keypad 9'),
   ('',                 'Keypad Clear', 'Keypad Clear', 'Keypad Clear', 'Keypad Clear'),
   ('',                 'Keypad 0',     'Keypad 0',     'Keypad 0',     'Keypad 0'),
   ('',                 'Keypad Enter', 'Keypad Enter', 'Keypad Enter', 'Keypad Enter'),
   ('',                 'Side left',    'Side left',    'Side left',    'Side left'),
   ('',                 'Side right',   'Side right',   'Side right',   'Side right'),
   ('',                 'Side top',     'Side top',     'Side top',     'Side top'),
   ('',                 'Disc N',       'Disc N',       'Disc N',       'Disc N'),
   ('',                 'Disc NNE',     'Disc NNE',     'Disc NNE',     'Disc NNE'),
   ('',                 'Disc NE',      'Disc NE',      'Disc NE',      'Disc NE'),
   ('',                 'Disc ENE',     'Disc ENE',     'Disc ENE',     'Disc ENE'),
   ('',                 'Disc E',       'Disc E',       'Disc E',       'Disc E'),
   ('',                 'Disc ESE',     'Disc ESE',     'Disc ESE',     'Disc ESE'),
   ('',                 'Disc SE',      'Disc SE',      'Disc SE',      'Disc SE'),
   ('',                 'Disc SSE',     'Disc SSE',     'Disc SSE',     'Disc SSE'),
   ('',                 'Disc S',       'Disc S',       'Disc S',       'Disc S'),
   ('',                 'Disc SSW',     'Disc SSW',     'Disc SSW',     'Disc SSW'),
   ('',                 'Disc SW',      'Disc SW',      'Disc SW',      'Disc SW'),
   ('',                 'Disc WSW',     'Disc WSW',     'Disc WSW',     'Disc WSW'),
   ('',                 'Disc W',       'Disc W',       'Disc W',       'Disc W'),
   ('',                 'Disc WNW',     'Disc WNW',     'Disc WNW',     'Disc WNW'),
   ('',                 'Disc NW',      'Disc NW',      'Disc NW',      'Disc NW'),
   ('',                 'Disc NNW',     'Disc NNW',     'Disc NNW',     'Disc NNW'));

  PropsCount : Array[0..4] Of Integer = (2,31,31,31,31);

  PropsLong  : Array[0..30,0..4] Of Byte =
  ((1, 1, 2, 3, 4),
   (2, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4),
   (0, 1, 2, 3, 4));

  PropsCaps : Array[0..30,0..4] Of LongWord =
  ((ifPower, ifKeypad_1, ifKeypad_1, ifKeypad_1, ifKeypad_1),
   (ifReset, ifKeypad_2, ifKeypad_2, ifKeypad_2, ifKeypad_2),
   (0,       ifKeypad_3, ifKeypad_3, ifKeypad_3, ifKeypad_3),
   (0,       ifKeypad_4, ifKeypad_4, ifKeypad_4, ifKeypad_4),
   (0,       ifKeypad_5, ifKeypad_5, ifKeypad_5, ifKeypad_5),
   (0,       ifKeypad_6, ifKeypad_6, ifKeypad_6, ifKeypad_6),
   (0,       ifKeypad_7, ifKeypad_7, ifKeypad_7, ifKeypad_7),
   (0,       ifKeypad_8, ifKeypad_8, ifKeypad_8, ifKeypad_8),
   (0,       ifKeypad_9, ifKeypad_9, ifKeypad_9, ifKeypad_9),
   (0,       ifKeypad_C, ifKeypad_C, ifKeypad_C, ifKeypad_C),
   (0,       ifKeypad_0, ifKeypad_0, ifKeypad_0, ifKeypad_0),
   (0,       ifKeypad_E, ifKeypad_E, ifKeypad_E, ifKeypad_E),
   (0,       ifSide_L,   ifSide_L,   ifSide_L,   ifSide_L),
   (0,       ifSide_R,   ifSide_R,   ifSide_R,   ifSide_R),
   (0,       ifSide_T,   ifSide_T,   ifSide_T,   ifSide_T),
   (0,       ifDisc_N,   ifDisc_N,   ifDisc_N,   ifDisc_N),
   (0,       ifDisc_NNE, ifDisc_NNE, ifDisc_NNE, ifDisc_NNE),
   (0,       ifDisc_NE,  ifDisc_NE,  ifDisc_NE,  ifDisc_NE),
   (0,       ifDisc_ENE, ifDisc_ENE, ifDisc_ENE, ifDisc_ENE),
   (0,       ifDisc_E,   ifDisc_E,   ifDisc_E,   ifDisc_E),
   (0,       ifDisc_ESE, ifDisc_ESE, ifDisc_ESE, ifDisc_ESE),
   (0,       ifDisc_SE,  ifDisc_SE,  ifDisc_SE,  ifDisc_SE),
   (0,       ifDisc_SSE, ifDisc_SSE, ifDisc_SSE, ifDisc_SSE),
   (0,       ifDisc_S,   ifDisc_S,   ifDisc_S,   ifDisc_S),
   (0,       ifDisc_SSW, ifDisc_SSW, ifDisc_SSW, ifDisc_SSW),
   (0,       ifDisc_SW,  ifDisc_SW,  ifDisc_SW,  ifDisc_SW),
   (0,       ifDisc_WSW, ifDisc_WSW, ifDisc_WSW, ifDisc_WSW),
   (0,       ifDisc_W,   ifDisc_W,   ifDisc_W,   ifDisc_W),
   (0,       ifDisc_WNW, ifDisc_WNW, ifDisc_WNW, ifDisc_WNW),
   (0,       ifDisc_NW,  ifDisc_NW,  ifDisc_NW,  ifDisc_NW),
   (0,       ifDisc_NNW, ifDisc_NNW, ifDisc_NNW, ifDisc_NNW));

  DigitalCells : Array[0..4] Of String = ('Buttons','Digital axes','Hats','Throttles','Analog axes');

{$R *.DFM}

procedure TfrmConfigure.btnOkClick(Sender: TObject);
Var
  I    : Integer;
  Intf : TInterface;
  LPT  : Integer;

begin
  // Locate the interfaces

  I := 0;
  While I < InterfaceHandler.Stack.Count Do
  Begin
    Intf := TInterface(InterfaceHandler.Stack.Objects[I]);
         If Intf.Name = 'Keyboard' Then KbdIntf.CopyTo(Intf)
    Else If Intf.Name = 'Mouse'    Then MouseIntf.CopyTo(Intf)
    Else If Intf.Name = 'Intv2PC'  Then ExternalIntf.CopyTo(Intf)
    Else DigitalIntf.CopyTo(Intf);

    If Intf.Name = 'Intv2PC' Then
    Begin
      For LPT := 0 To 2 Do
      Begin
        If (ExternalIntf.LPTCap[LPT].Cont[1] <> 0) Or
           (ExternalIntf.LPTCap[LPT].Cont[2] <> 0) Then Intv2PCPort1 := LPTPorts[LPT];//MemW[0:$408 + LPT * 2];
        If (ExternalIntf.LPTCap[LPT].Cont[3] <> 0) Or
           (ExternalIntf.LPTCap[LPT].Cont[4] <> 0) Then Intv2PCPort2 := LPTPorts[LPT];//MemW[0:$408 + LPT * 2];
      End; { For LPT }
    End;

    Inc(I);
  End; // While
  Close;
end;

procedure TfrmConfigure.FormShow(Sender: TObject);
Var
  I,J  : Integer;
  Intf : TInterface;

Begin

  // Locate the interfaces

  I := 0;
  While I < InterfaceHandler.Stack.Count Do
  Begin
    Intf := TInterface(InterfaceHandler.Stack.Objects[I]);
         If Intf.Name = 'Keyboard' Then Intf.CopyTo(KbdIntf)
    Else If Intf.Name = 'Mouse'    Then Intf.CopyTo(MouseIntf)
    Else If Intf.Name = 'Intv2PC'  Then Intf.CopyTo(ExternalIntf)
    Else Intf.CopyTo(DigitalIntf);
    Inc(I);
  End; // While

  sgController.DefaultRowHeight := Canvas.TextHeight('Mg') + 6;
  sgProps.DefaultRowHeight := Canvas.TextHeight('Mg') + 6;
  SetGridWidths;
  Updating := False;
  For I := 0 To 6 Do
   For J := 0 To sgKeyboard.ColCount - 1 Do sgKeyboard.Cells[J,I] := CellText[I,J];
  sgKeyboard.ColWidths[14] := 10;
  sgKeyboard.ColWidths[18] := 10;
  sgKeyboard.RowHeights[1] := 10;
  pcUserChange(Self);
  pcControllerChange(Self);
  Timer1.Enabled := True;
  FillChar(OldKeys,SizeOf(OldKeys),#0);
end;

Procedure TfrmConfigure.SetKeyboardSize;
Var
  I,W,H : Integer;
  Ok    : Boolean;

Begin
  W := Trunc((sgKeyboard.Width - 8) / 22.5);
  H := Trunc(W * (40 / 45));
  If (H * 6.25) + 8 > sgKeyboard.Height Then
  Begin
    H := Trunc((sgKeyboard.Height - 8) / 6.25);
    W := Trunc(H * (45 / 40));
  End;

  // Resize text font in keyboard grid

  If W < StrWid + 4 Then
  Begin
    sgKeyboard.Canvas.Font.Name := 'Arial';
    sgKeyboard.Canvas.Font.Size := 1;
    I                           := 1;
    Repeat
      sgKeyboard.Canvas.Font.Size := I + 1;
      Ok := (sgKeyboard.Canvas.TextWidth('Pause') < W - 2);
      If Ok Then Inc(I);
    Until Not Ok;
    sgKeyboard.Canvas.Font.Size := I;
  End
  Else
  Begin
    sgKeyboard.Canvas.Font.Name := 'MS Sans Serif';
    sgKeyboard.Canvas.Font.Size := 8;
  End;

  I := Round(H * 6.25 + 8);
  sgKeyboard.Height           := I;
  sgKeyboard.DefaultColWidth  := W;
  sgKeyboard.DefaultRowHeight := H;
  I := H Div 4;
  If I < 1 Then I := 1;
  sgKeyboard.ColWidths[14] := I;
  sgKeyboard.ColWidths[18] := I;
  sgKeyboard.RowHeights[1] := I;
  pcUser.Repaint;
End; // TfrmConfigure.SetKeyboardSize

procedure TfrmConfigure.sgKeyboardDrawCell(Sender: TObject; ACol,
  ARow: Integer; Rect: TRect; State: TGridDrawState);
Var
  W       : Integer;
  H       : Integer;
  CF,CB   : TColor;
  Row,Col : Integer;
  I       : Integer;
  X,Y     : Integer;

begin
  Row := ARow;
  Col := ACol;
  GetBaseRowCol(Row,Col);
  If (CellWidth[ARow,ACol]  > 0) Or
     (CellHeight[ARow,ACol] > 0) Then
  Begin
    W := Rect.Right  - Rect.Left;
    H := Rect.Bottom - Rect.Top;
    Dec(Rect.Left,(ACol - Col) * W);
    Dec(Rect.Top, (ARow - Row) * H);
    ARow := Row;
    ACol := Col;

    Rect.Right  := Rect.Left + CellWidth[ARow,ACol]  * W;
    Rect.Bottom := Rect.Top  + CellHeight[ARow,ACol] * H;

    If CellChangeable[ARow,ACol] Then
    Begin
      If (ARow = sgKeyboard.Row) And (ACol = sgKeyboard.Col) Then
      Begin
        CF := clHighlightText;
        CB := clHighlight;
      End
      Else
      Begin
        CF := clBlack;
        CB := sgKeyboard.Color;
      End;
    End
    Else
    Begin
      If (ARow = sgKeyboard.Row) And (ACol = sgKeyboard.Col) Then
      Begin
        CF := clHighlightText;
        CB := clHighlight;
      End
      Else
      Begin
        CF := clBlack;
        CB := sgKeyboard.FixedColor;
      End;
    End;

    If (CellText[ARow,ACol] <> '') And
       (KbdIntf <> Nil)            And
       KbdIntf.UsedCapability(KbdIntf.KeyCap[CellScanCode[ARow,ACol]]) And
       (CB <> clHighlight) Then
    Begin
      If CellChangeable[ARow,ACol]
       Then CB := $00E0FFE0
       Else CB := $00C0F0C0;
    End;

    // Draw outline

    sgKeyboard.Canvas.MoveTo(Rect.Left,Rect.Bottom - 1);
    sgKeyboard.Canvas.Pen.Color := clbtnHighlight;
    sgKeyboard.Canvas.LineTo(Rect.Left,Rect.Top);
    sgKeyboard.Canvas.LineTo(Rect.Right - 1,Rect.Top);
    sgKeyboard.Canvas.Pen.Color := clBtnShadow;
    sgKeyboard.Canvas.LineTo(Rect.Right - 1,Rect.Bottom - 1);
    sgKeyboard.Canvas.LineTo(Rect.Left,Rect.Bottom - 1);

    // Fill center

    sgKeyboard.Canvas.Pen.Color   := CB;
    sgKeyboard.Canvas.Brush.Color := CB;
    sgKeyboard.Canvas.Brush.Style := bsSolid;
    sgKeyboard.Canvas.Font.Color  := CF;
    sgKeyboard.Canvas.Rectangle(Rect.Left + 1,Rect.Top + 1,Rect.Right - 1,Rect.Bottom - 1);

    // Draw text

    sgKeyboard.Canvas.TextOut(Rect.Left + 2,Rect.Top + 2, CellText[ARow,ACol]);

    For I := 0 To 255 Do
    Begin
      If OldKeys[I] Then
      Begin
        If CellScanCode[ARow,ACol] = I Then
        Begin
          sgKeyboard.Canvas.Pen.Color   := clRed;
          sgKeyboard.Canvas.Brush.Color := clRed;
          X := Rect.Left + ((Rect.Right  - Rect.Left) Div 2);
          Y := Rect.Top  + ((Rect.Bottom - Rect.Top)  Div 2);
          sgKeyboard.Canvas.Ellipse(X - 2,Y - 2,X + 2,Y + 2);
        End;
      End;
    End; // For I
  End
  Else
  Begin
    sgKeyboard.Canvas.Pen.Color   := sgKeyboard.Color;
    sgKeyboard.Canvas.Brush.Color := sgKeyboard.Color;
    sgKeyboard.Canvas.Rectangle(Rect.Left,Rect.Top,Rect.Right,Rect.Bottom);
  End;
end; // TfrmConfigure.sgKeyboardDrawCell

procedure TfrmConfigure.sgKeyboardSelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
Var C,R,C1,R1,C2,R2: Integer;
begin
  If Not Updating Then
  Begin

    // Paint the old one

    C  := sgKeyboard.Col;
    R  := sgKeyboard.Row;
    C1 := ACol;
    R1 := ARow;
    GetBaseRowCol(R,C);
    GetBaseRowCol(R1,C1);

    CanSelect := False;  // We will control selection
    Updating  := True;   // Prevent recursion

    // Cycle through the old key cells if necessary to force repaint

    If (R1 <> R) Or (C1 <> C) Then
     For C2 := 0 To CellWidth[R,C] - 1 Do
      For R2 := 0 To CellHeight[R,C] - 1 Do
      Begin
        sgKeyboard.Row := R + R2;
        sgKeyboard.Col := C + C2;
        sgKeyboard.Invalidate;
        sgKeyboard.Repaint;
      End; // For R2

    // Set to the new key cell

    If (R1 <> sgKeyboard.Row) Or (C1 <> sgKeyboard.Col) Then
    Begin
      sgKeyboard.Row := R1;
      sgKeyboard.Col := C1;
      sgKeyboard.Invalidate;
      sgKeyboard.Repaint;
    End;
    Updating := False;
  End
  Else CanSelect := True;
end;

Procedure TfrmConfigure.GetBaseRowCol(Var Row,Col: Integer);
Var C,R: Integer;
Begin
  If (CellWidth[Row,Col]  > 0) Or
     (CellHeight[Row,Col] > 0) Then
  Begin
    If CellText[Row,Col] = '' Then
    Begin
      C   := Col - CellWidth[Row,Col];
      R   := Row - CellHeight[Row,Col];
      Row := R;
      Col := C;
    End;
  End;
End; { TfrmConfigure.GetBaseRowCol }

procedure TfrmConfigure.pcUserChange(Sender: TObject);
Var
  NumCons  : Integer;
  I,J      : LongInt;
  SlotMap  : LongInt;
  Vendor   : String;
  Product  : String;

begin
  If pcUser.ActivePage = tbDigital Then
  Begin
    If DigitalIntf <> Nil Then
    Begin
      I := 0;
      Repeat
        DigitalIntf.RefreshControllers;
        SlotMap := DigitalIntf.GetSlotMap;
        Inc(I);
      Until (I > 31) Or (SlotMap <> 0);

      { Find out how many controllers are connected }

      J       := 1;
      NumCons := 0;
      For I := 0 To 31 Do
      Begin
        If (SlotMap And J) <> 0 Then Inc(NumCons);
        J := J Shl 1;
      End; { For I }

      { Initialize the menu and get the controller information }

      cbController.Clear;
      If NumCons > 0 Then
      Begin
        J := 1;
        For I := 0 To 31 Do
        Begin
          If (SlotMap And J) <> 0 Then
          Begin
            Vendor  := DigitalIntf.GetVendorName(I);
            Product := DigitalIntf.GetProductName(I);
            While (Vendor <> '') And (Vendor[1] < ' ') Do
             Vendor := Copy(Vendor,2,Length(Vendor) - 1);
            While (Product <> '') And (Product[1] < ' ') Do
             Product := Copy(Product,2,Length(Product) - 1);
            cbController.Items.Add(Vendor + ' ' + Product);
          End;
          J := J Shl 1;
        End; { For I }
        cbController.ItemIndex := 0;
        LoadDigitalInfo;
      End;
    End;
  End;
  LoadControllerInfo;  
end;

procedure TfrmConfigure.pcControllerChange(Sender: TObject);
begin
  LoadControllerInfo;
end;

Procedure TfrmConfigure.LoadControllerInfo;
Var
  Intf  : TInterface;
  I     : Integer;
  R,C   : Integer;

Begin
       If pcUser.ActivePage = tbKeyboard Then Intf := KbdIntf
  Else If pcUser.ActivePage = tbMouse    Then Intf := MouseIntf
  Else If pcUser.ActivePage = tbExternal Then Intf := ExternalIntf
  Else Intf := DigitalIntf;
  If Intf <> Nil Then
  Begin
    FillChar(Cap.Cont,SizeOf(Cap.Cont),#0);
    If Intf.Name = 'Keyboard' Then
    Begin
      R := sgKeyboard.Row;
      C := sgKeyboard.Col;
      If (R >= 0) And (C >= 0) And (CellText[R,C] <> '') Then
      Begin
        Cap := TKeyboardInterface(Intf).KeyCap[CellScanCode[R,C]];
      End;
    End
    Else If Intf.Name = 'Mouse' Then
    Begin
           If rbMouseXAxis.Checked  Then Cap := TMouseInterface(Intf).MouseX.Cap
      Else If rbMouseYAxis.Checked  Then Cap := TMouseInterface(Intf).MouseY.Cap
      Else If rbMouseLeft.Checked   Then Cap := TMouseInterface(Intf).MouseBtn[1]
      Else If rbMouseRight.Checked  Then Cap := TMouseInterface(Intf).MouseBtn[2]
      Else If rbMouseMiddle.Checked Then Cap := TMouseInterface(Intf).MouseBtn[3];
    End
    Else If Intf.Name = 'Intv2PC' Then
    Begin
           If rbLPT1.Checked Then Cap := TExternalInterface(Intf).LPTCap[0]
      Else If rbLPT2.Checked Then Cap := TExternalInterface(Intf).LPTCap[1]
      Else If rbLPT3.Checked Then Cap := TExternalInterface(Intf).LPTCap[2];
    End
    Else
    Begin
      I := cbController.ItemIndex;
      If I >= 0 Then
      Begin
        If (sgController.Row >= 1) And (sgController.Col >= 0) Then
        Begin
          Case sgController.Col Of
            0: Cap := TDigitalInterface(Intf).Cap[I + 1].Btn[sgController.Row];
            1: Cap := TDigitalInterface(Intf).Cap[I + 1].Axis[sgController.Row];
            2: Cap := TDigitalInterface(Intf).Cap[I + 1].Hat[sgController.Row];
            3: Cap := TDigitalInterface(Intf).Cap[I + 1].Thr[sgController.Row].Cap;
            4: Cap := TDigitalInterface(Intf).Cap[I + 1].Ana[sgController.Row].Cap;
          End; { Case }
        End;
      End;
    End;
    sgProps.Invalidate;
    sgProps.Repaint;
  End;
End; // TfrmConfigure.LoadControllerInfo

Procedure TfrmConfigure.ChangeCapability;
Var
  Intf  : TInterface;
  I     : Integer;
  R,C   : Integer;

Begin
        If pcUser.ActivePage = tbKeyboard Then Intf := KbdIntf
   Else If pcUser.ActivePage = tbMouse    Then Intf := MouseIntf
   Else If pcUser.ActivePage = tbExternal Then Intf := ExternalIntf
   Else Intf := DigitalIntf;
  If Intf <> Nil Then
  Begin
    If Intf.Name = 'Keyboard' Then
    Begin
      R := sgKeyboard.Row;
      C := sgKeyboard.Col;
      If (R >= 0) And (C >= 0) And (CellText[R,C] <> '') Then
      Begin
        TKeyboardInterface(Intf).KeyCap[CellScanCode[R,C]] := Cap;
      End;
    End
    Else If Intf.Name = 'Mouse' Then
    Begin
           If rbMouseXAxis.Checked  Then TMouseInterface(Intf).MouseX.Cap  := Cap
      Else If rbMouseYAxis.Checked  Then TMouseInterface(Intf).MouseY.Cap  := Cap
      Else If rbMouseLeft.Checked   Then TMouseInterface(Intf).MouseBtn[1] := Cap
      Else If rbMouseRight.Checked  Then TMouseInterface(Intf).MouseBtn[2] := Cap
      Else If rbMouseMiddle.Checked Then TMouseInterface(Intf).MouseBtn[3] := Cap;
    End
    Else If Intf.Name = 'Intv2PC' Then
    Begin
           If rbLPT1.Checked Then TExternalInterface(Intf).LPTCap[0] := Cap
      Else If rbLPT2.Checked Then TExternalInterface(Intf).LPTCap[1] := Cap
      Else If rbLPT3.Checked Then TExternalInterface(Intf).LPTCap[2] := Cap;
    End
    Else
    Begin
      I := cbController.ItemIndex;
      If I >= 0 Then
      Begin
        If (sgController.Row >= 1) And (sgController.Col >= 0) Then
        Begin
          Case sgController.Col Of
            0: TDigitalInterface(Intf).Cap[I + 1].Btn[sgController.Row]     := Cap;
            1: TDigitalInterface(Intf).Cap[I + 1].Axis[sgController.Row]    := Cap;
            2: TDigitalInterface(Intf).Cap[I + 1].Hat[sgController.Row]     := Cap;
            3: TDigitalInterface(Intf).Cap[I + 1].Thr[sgController.Row].Cap := Cap;
            4: TDigitalInterface(Intf).Cap[I + 1].Ana[sgController.Row].Cap := Cap;
          End; { Case }
        End;
      End;
    End;
  End;
End; // TfrmConfigure.ChangeCapability

procedure TfrmConfigure.sgKeyboardClick(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.rbMouseXAxisClick(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.rbMouseYAxisClick(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.rbMouseLeftClick(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.rbMouseRightClick(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.rbMouseMiddleClick(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.cbControllerChange(Sender: TObject);
begin
  LoadDigitalInfo;
  LoadControllerInfo;
end;

Procedure TfrmConfigure.LoadDigitalInfo;
Var
  Con,I : Integer;
  Cap   : TCapability;
  Info  : TIntvControls;

Begin
  If (DigitalIntf <> Nil) And (cbController.ItemIndex >= 0) Then
  Begin
    Con := cbController.ItemIndex + 1;
    DigitalIntf.GetControllerInfo(Cap,Info);
    I := 0;
    If DigitalIntf.Cap[Con].NumBtns > I Then I := DigitalIntf.Cap[Con].NumBtns;
    If DigitalIntf.Cap[Con].NumAxes > I Then I := DigitalIntf.Cap[Con].NumAxes;
    If DigitalIntf.Cap[Con].NumHats > I Then I := DigitalIntf.Cap[Con].NumHats;
    If DigitalIntf.Cap[Con].NumThrs > I Then I := DigitalIntf.Cap[Con].NumThrs;
    If DigitalIntf.Cap[Con].NumAnas > I Then I := DigitalIntf.Cap[Con].NumAnas;
    sgController.RowCount := I + 1;

    // Load digital controller string grid

    For I := 0 To 4 Do sgController.Cells[0,I] := DigitalCells[I];
    For I := 1 To DigitalIntf.Cap[Con].NumBtns Do sgController.Cells[I,0] := 'Button '       + IntToStr(I);
    For I := 1 To DigitalIntf.Cap[Con].NumAxes Do sgController.Cells[I,1] := 'Digital axis ' + IntToStr(I);
    For I := 1 To DigitalIntf.Cap[Con].NumHats Do sgController.Cells[I,2] := 'Hat '          + IntToStr(I);
    For I := 1 To DigitalIntf.Cap[Con].NumThrs Do sgController.Cells[I,3] := 'Throttle '     + IntToStr(I);
    For I := 1 To DigitalIntf.Cap[Con].NumAnas Do sgController.Cells[I,4] := 'Analog axis '  + IntToStr(I);
  End;
End; // TfrmConfigure.LoadDigitalInfo

procedure TfrmConfigure.FormCreate(Sender: TObject);
Var I,J: Integer;
begin
  KbdIntf      := TKeyboardInterface.Create;
  MouseIntf    := TMouseInterface.Create;
  DigitalIntf  := TDigitalInterface.Create('Digital');
  ExternalIntf := TExternalInterface.Create;
  Caption   := 'PCAE ' + Version;
  FillChar(Cap.Cont,SizeOf(Cap.Cont),#0);

  // Load properties string grid

  For I := 0 To 4 Do
   For J := 0 To 31 Do sgProps.Cells[J,I] := PropsCells[J,I];

  // Load digital controller string grid

  For I := 0 To 4 Do sgController.Cells[0,I] := DigitalCells[I];
  StrWid := Canvas.TextWidth('Pause');
end;

procedure TfrmConfigure.sgPropsDrawCell(Sender: TObject; ACol,
  ARow: Integer; Rect: TRect; State: TGridDrawState);
Var
  L   : LongInt;
  C   : TColor;
  Y   : Integer;
  Img : TIcon;
  I   : Integer;

begin
  I := PropsLong[ARow - 1,ACol];
  If I > 0 Then L := Cap.Cont[I] Else L := 0;
  Y := Rect.Top + ((Rect.Bottom - Rect.Top - sgProps.Canvas.TextHeight('Mg')) Div 2);
  If ARow = 0 Then
  Begin
    sgProps.Canvas.Pen.Color   := clBtnHighlight;
    sgProps.Canvas.MoveTo(Rect.Left,Rect.Bottom - 1);
    sgProps.Canvas.LineTo(Rect.Left,Rect.Top);
    sgProps.Canvas.LineTo(Rect.Right,Rect.Top);
    sgProps.Canvas.Pen.Color   := clBtnShadow;
    sgProps.Canvas.MoveTo(Rect.Left,Rect.Bottom - 1);
    sgProps.Canvas.LineTo(Rect.Right - 1,Rect.Bottom - 1);
    sgProps.Canvas.LineTo(Rect.Right - 1,Rect.Top);
    sgProps.Canvas.Pen.Color   := sgProps.FixedColor;
    sgProps.Canvas.Brush.Color := sgProps.FixedColor;
    sgProps.Canvas.Rectangle(Rect.Left + 1,Rect.Top + 1,Rect.Right,Rect.Bottom);

    Img := TIcon.Create;
    If ACol = 0
     Then ImageList3.GetIcon(ACol,Img)
     Else If Acol <= 2
      Then ImageList3.GetIcon(1,Img)
      Else ImageList3.GetIcon(2,Img);
    sgProps.Canvas.Draw(Rect.Left + 2,Rect.Top + 2,Img);
    Img.Free;

    sgProps.Canvas.Font.Color := clBlack;
    sgProps.Canvas.TextOut(Rect.Left + 28,Y,sgProps.Cells[ARow,ACol]);
  End
  Else
  Begin
    If L And PropsCaps[ARow - 1,ACol] <> 0
     Then C := $00FFE0E0
     Else C := Color;
    sgProps.Canvas.Pen.Color   := C;
    sgProps.Canvas.Brush.Color := C;
    sgProps.Canvas.Rectangle(Rect);
    If (AxisOnly And
        ((ARow <= 15) Or
         (ACol  = 0))) Or
       (PairOnly And
        (ACol = 0))
     Then sgProps.Canvas.Font.Color  := clGray
     Else sgProps.Canvas.Font.Color  := clBlack;
    sgProps.Canvas.TextOut(Rect.Left + 2,Y,sgProps.Cells[ARow,ACol]);
  End;
end;

Function TfrmConfigure.PairOnly: Boolean;
Begin
  PairOnly := (pcUser.ActivePage = tbExternal);
End; // TfrmConfigure.PairOnly

Function TfrmConfigure.AxisOnly: Boolean;
Begin
  AxisOnly := ((pcUser.ActivePage = tbMouse) And
               (rbMouseXAxis.Checked Or rbMouseYAxis.Checked)) Or
              ((pcUser.ActivePage = tbDigital) And
               (sgController.Col <> 0));
End; // TfrmConfigure.AxisOnly

Function TfrmConfigure.CannotChange: Boolean;
Begin
  CannotChange := (pcUser.ActivePage = tbKeyboard) And
                  Not CellChangeable[sgKeyboard.Row,sgKeyboard.Col];
End; // TfrmConfigure.CannotChange

procedure TfrmConfigure.sgPropsSelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
Var
  I   : Integer;
  J,L : LongWord;
  C   : LongWord;

begin
  CanSelect := True;
  If ARow >= 1 Then
  Begin
    I := PropsLong[ARow - 1,ACol];
    If I > 0 Then L := Cap.Cont[I] Else L := 0;
    C := PropsCaps[ARow - 1,ACol];
    If CannotChange Then
    Begin
      CanSelect := False;
      Exit;
    End;

    If PairOnly Then
    Begin
      If ACol > 0 Then
      Begin
        If ((Cap.Cont[((ACol - 1) And $FE) + 1] And cmController) <> 0) Or
           ((Cap.Cont[((ACol - 1) And $FE) + 2] And cmController) <> 0) Then
        Begin
          Cap.Cont[((ACol - 1) And $FE) + 1] := 0;
          Cap.Cont[((ACol - 1) And $FE) + 2] := 0;
        End
        Else
        Begin
          Cap.Cont[((ACol - 1) And $FE) + 1] := cmController;
          Cap.Cont[((ACol - 1) And $FE) + 2] := cmController;
          If ACol < 3 Then
          Begin
            Cap.Cont[3] := 0;
            Cap.Cont[4] := 0;
          End
          Else
          Begin
            Cap.Cont[1] := 0;
            Cap.Cont[2] := 0;
          End;
        End;
      End;  
    End
    Else
    Begin
      If AxisOnly Then
      Begin
        If (ACol <> 0) And (ARow > 15) Then C := imDisc;
      End;

      J := 0;
      For I := 0 To PropsCount[ACol] - 1 Do J := J Or PropsCaps[I,ACol];
      If (L And C) <> 0
       Then L := L Xor C
       Else L := (L And (Not J)) Or C;
      I := PropsLong[ARow - 1,ACol];
      If I > 0 Then Cap.Cont[I] := L;
    End;
    ChangeCapability;
    sgProps.Invalidate;
    sgProps.Repaint;
  End;
end;

procedure TfrmConfigure.sgControllerDrawCell(Sender: TObject; ACol,
  ARow: Integer; Rect: TRect; State: TGridDrawState);
Var
  CF,CB : TColor;
  I     : Integer;
  Y     : Integer;
  X1,Y1 : Integer;
  Img   : TIcon;
  W     : Integer;
  W1    : Integer;

begin
  Y  := Rect.Top + ((Rect.Bottom - Rect.Top - sgController.Canvas.TextHeight('Mg')) Div 2);
  X1 := Rect.Left + 10;
  Y1 := Rect.Top + ((Rect.Bottom - Rect.Top) Div 2);
  If ARow = 0 Then
  Begin
    sgController.Canvas.Pen.Color   := clBtnHighlight;
    sgController.Canvas.MoveTo(Rect.Left,Rect.Bottom - 1);
    sgController.Canvas.LineTo(Rect.Left,Rect.Top);
    sgController.Canvas.LineTo(Rect.Right,Rect.Top);
    sgController.Canvas.Pen.Color   := clBtnShadow;
    sgController.Canvas.MoveTo(Rect.Left,Rect.Bottom - 1);
    sgController.Canvas.LineTo(Rect.Right - 1,Rect.Bottom - 1);
    sgController.Canvas.LineTo(Rect.Right - 1,Rect.Top);
    sgController.Canvas.Pen.Color   := sgController.FixedColor;
    sgController.Canvas.Brush.Color := sgController.FixedColor;
    sgController.Canvas.Rectangle(Rect.Left + 1,Rect.Top + 1,Rect.Right,Rect.Bottom);

    Img := TIcon.Create;
    ImageList2.GetIcon(ACol,Img);
    sgController.Canvas.Draw(Rect.Left + 2,Rect.Top + 2,Img);
    Img.Free;

    sgController.Canvas.Font.Color := clBlack;
    sgController.Canvas.TextOut(Rect.Left + 28,Y,sgController.Cells[ARow,ACol]);
  End
  Else
  Begin
    If gdSelected In State Then
    Begin
      CF := clHighlightText;
      CB := clHighlight;
    End
    Else
    Begin
      CF := clBlack;
      CB := Color;
    End;
    sgController.Canvas.Pen.Color   := CB;
    sgController.Canvas.Brush.Color := CB;
    sgController.Canvas.Font.Color  := CF;
//    sgController.Canvas.Rectangle(Rect);
    I := cbController.ItemIndex;
    If (DigitalIntf <> Nil) And (I >= 0) Then
    Begin
      Case ACol Of
        0: If ARow <= DigitalIntf.Cap[I + 1].NumBtns Then
           Begin
             If DigitalIntf.UsedCapability(DigitalIntf.Cap[I + 1].Btn[ARow]) Then
             Begin
               If CB <> clHighlight Then CB := $00E0FFE0;
               sgController.Canvas.Pen.Color   := CB;
               sgController.Canvas.Brush.Color := CB;
             End;
             sgController.Canvas.Rectangle(Rect);
             If (DigitalIntf.ContInfo[I + 1].Buttons And (1 Shl (ARow - 1))) <> 0 Then
             Begin
               sgController.Canvas.Pen.Color   := clRed;
               sgController.Canvas.Brush.Color := clRed;
               sgController.Canvas.Ellipse(X1 - 2,Y1 - 2,X1 + 2,Y1 + 2);
             End;
           End;
        1: Begin
             If ARow <= DigitalIntf.Cap[I + 1].NumAxes Then
             Begin
               If DigitalIntf.UsedCapability(DigitalIntf.Cap[I + 1].Axis[ARow]) Then
               Begin
                 If CB <> clHighlight Then CB := $00E0FFE0;
                 sgController.Canvas.Pen.Color   := CB;
                 sgController.Canvas.Brush.Color := CB;
               End;
               sgController.Canvas.Rectangle(Rect);
               sgController.Canvas.Pen.Color   := clRed;
               sgController.Canvas.Brush.Color := clRed;
               W  := DigitalIntf.ContInfo[I + 1].Axis[ARow];
               Inc(X1,8 * ((W - 10) Div 10));
               sgController.Canvas.Ellipse(X1 - 2,Y1 - 2,X1 + 2,Y1 + 2);
             End
             Else sgController.Canvas.Rectangle(Rect);
           End;
        2: Begin
             If ARow <= DigitalIntf.Cap[I + 1].NumHats Then
             Begin
               If DigitalIntf.UsedCapability(DigitalIntf.Cap[I + 1].Hat[ARow]) Then
               Begin
                 If CB <> clHighlight Then CB := $00E0FFE0;
                 sgController.Canvas.Pen.Color   := CB;
                 sgController.Canvas.Brush.Color := CB;
               End;
               sgController.Canvas.Rectangle(Rect);
               sgController.Canvas.Pen.Color   := clRed;
               sgController.Canvas.Brush.Color := clRed;
               W  := DigitalIntf.ContInfo[I + 1].Hat[ARow];
               Inc(X1,8 * ((W - 10) Div 10));
               sgController.Canvas.Ellipse(X1 - 2,Y1 - 2,X1 + 2,Y1 + 2);
             End
             Else sgController.Canvas.Rectangle(Rect);
           End;
        3: Begin
             If ARow <= DigitalIntf.Cap[I + 1].NumThrs Then
             Begin
               If DigitalIntf.UsedCapability(DigitalIntf.Cap[I + 1].Thr[ARow].Cap) Then
               Begin
                 If CB <> clHighlight Then CB := $00E0FFE0;
                 sgController.Canvas.Pen.Color   := CB;
                 sgController.Canvas.Brush.Color := CB;
               End;
               sgController.Canvas.Rectangle(Rect);
               W1 := DigitalIntf.Cap[I + 1].Thr[ARow].Max - DigitalIntf.Cap[I + 1].Thr[ARow].Min;
               If W1 > 0 Then
               Begin
                 W := DigitalIntf.ContInfo[I + 1].Throttle[ARow];
                 sgController.Canvas.Pen.Color   := clRed;
                 sgController.Canvas.Brush.Color := clRed;
                 Inc(X1,Round((W - DigitalIntf.Cap[I + 1].Thr[ARow].Min - W1 Div 2) * (8 / W1)));
                 sgController.Canvas.Ellipse(X1 - 2,Y1 - 2,X1 + 2,Y1 + 2);
               End;
             End
             Else sgController.Canvas.Rectangle(Rect);
           End;
        4: Begin
             If ARow <= DigitalIntf.Cap[I + 1].NumAnas Then
             Begin
               If DigitalIntf.UsedCapability(DigitalIntf.Cap[I + 1].Ana[ARow].Cap) Then
               Begin
                 If CB <> clHighlight Then CB := $00E0FFE0;
                 sgController.Canvas.Pen.Color   := CB;
                 sgController.Canvas.Brush.Color := CB;
               End;
               sgController.Canvas.Rectangle(Rect);
               W1 := DigitalIntf.Cap[I + 1].Ana[ARow].Max - DigitalIntf.Cap[I + 1].Ana[ARow].Min;
               If W1 > 0 Then
               Begin
                 W := DigitalIntf.ContInfo[I + 1].Analog[ARow];
                 sgController.Canvas.Pen.Color   := clRed;
                 sgController.Canvas.Brush.Color := clRed;
                 Inc(X1,Round((W - DigitalIntf.Cap[I + 1].Ana[ARow].Min - W1 Div 2) * (8 / W1)));
                 sgController.Canvas.Ellipse(X1 - 2,Y1 - 2,X1 + 2,Y1 + 2);
               End;
             End
             Else sgController.Canvas.Rectangle(Rect);
           End;
      End; { Case }
    End;
    sgController.Canvas.Pen.Color   := CB;
    sgController.Canvas.Brush.Color := CB;
    sgController.Canvas.TextOut(Rect.Left + 20,Y,sgController.Cells[ARow,ACol]);
  End;
end;

procedure TfrmConfigure.sgControllerClick(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.FormResize(Sender: TObject);
begin
  pcUser.Width   := Width  - 24;
  sgProps.Width  := Width  - 24;
  sgProps.Height := Height - sgProps.Top - 75;
  btnOk.Top      := Height - 68;
  btnOk.Left     := Width  - 89;
  SetGridWidths;
  SetKeyboardSize;
  sgKeyboard.Invalidate;
  sgKeyboard.Refresh;  
end;

Procedure TfrmConfigure.SetGridWidths;
Var I,J,S: Integer;
Begin
  S := GetSystemMetrics(SM_CXHSCROLL);
  sgController.Width := pcUser.Width - 8;
  sgController.DefaultColWidth := (sgController.Width - S) Div sgController.ColCount;
  sgProps.DefaultColWidth      := (sgProps.Width - S)      Div sgProps.ColCount;

  J := 0;
  For I := 0 To sgController.ColCount - 2 Do Inc(J,sgController.ColWidths[I] + sgController.GridLineWidth);
  sgController.ColWidths[sgController.ColCount - 1] := sgController.Width - J - S - 4;

  J := 0;
  For I := 0 To sgProps.ColCount - 2 Do Inc(J,sgProps.ColWidths[I] + sgProps.GridLineWidth);
  sgProps.ColWidths[sgProps.ColCount - 1] := sgProps.Width - J - S - 4;
End; // TfrmConfigure.SetGridWidths

procedure TfrmConfigure.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Timer1.Enabled := False;
end;

procedure TfrmConfigure.Timer1Timer(Sender: TObject);
Var
  Cap      : TCapability;
  Info     : TIntvControls;
  ContInfo : Array[1..Max_Digital_Con] Of TDigitalInfoRec;
  Diff     : Boolean;
  I,J      : Integer;

begin
  If pcUser.ActivePage = tbKeyboard Then
  Begin
    If KbdIntf <> Nil Then
    Begin
      KbdIntf.GetControllerInfo(Cap,Info);
      I    := 0;
      Diff := False;
      While (I <= 255) And Not Diff Do
      Begin
        If KbdIntf.Keys[I] <> OldKeys[I] Then Diff := True Else Inc(I);
      End; // While
      For I := 0 To 255 Do OldKeys[I] := KbdIntf.Keys[I];
      If Diff Then
      Begin
        sgKeyboard.Invalidate;
        sgKeyboard.Repaint;
      End;
    End;
  End
  Else If pcUser.ActivePage = tbMouse Then
  Begin
//    If MouseIntf <> Nil Then MouseIntf.GetControllerInfo(Cap,Info);
  End
  Else If pcUser.ActivePage = tbExternal Then
  Begin
  End
  Else
  Begin
    If DigitalIntf <> Nil Then
    Begin
      For I := 1 To Max_Digital_Con Do ContInfo[I] := DigitalIntf.ContInfo[I];
      DigitalIntf.GetControllerInfo(Cap,Info);
      I    := 1;
      Diff := False;
      While (I <= Max_Digital_Con) And Not Diff Do
      Begin
        If ContInfo[I].Buttons <> DigitalIntf.ContInfo[I].Buttons Then Diff := True Else
        Begin
          J := 1;
          While (J < Max_Digital_Axis) And Not Diff Do
          Begin
            If ContInfo[I].Axis[J] <> DigitalIntf.ContInfo[I].Axis[J] Then Diff := True;
            Inc(J);
          End; // While

          J := 1;
          While (J < Max_Digital_Hat) And Not Diff Do
          Begin
            If ContInfo[I].Hat[J] <> DigitalIntf.ContInfo[I].Hat[J] Then Diff := True;
            Inc(J);
          End; // While

          J := 1;
          While (J < Max_Digital_Thr) And Not Diff Do
          Begin
            If ContInfo[I].Throttle[J] <> DigitalIntf.ContInfo[I].Throttle[J] Then Diff := True;
            Inc(J);
          End; // While

          J := 1;
          While (J < Max_Digital_Ana) And Not Diff Do
          Begin
            If ContInfo[I].Analog[J] <> DigitalIntf.ContInfo[I].Analog[J] Then Diff := True;
            Inc(J);
          End; // While
        End;
        Inc(I);
      End; { While }
      If Diff Then
      Begin
        sgController.Invalidate;
        sgController.Repaint;
      End;  
    End;
  End;
end;

procedure TfrmConfigure.FormDestroy(Sender: TObject);
begin
  KbdIntf.Free;
  MouseIntf.Free;
  DigitalIntf.Free;
  ExternalIntf.Free;
end;

procedure TfrmConfigure.rbLPT1Click(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.rbLPT2Click(Sender: TObject);
begin
  LoadControllerInfo;
end;

procedure TfrmConfigure.rbLPT3Click(Sender: TObject);
begin
  LoadControllerInfo;
end;

end.

// ----------------------------------------------------------------------
// IntvWin - Mattel Intellivision Emulator
// Copyright (C) 2000 John Dullea
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ----------------------------------------------------------------------
