home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 23 / IOPROG_23.ISO / SOFT / DELPHIX.ZIP / Source / DXInput.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-10-06  |  59.3 KB  |  2,292 lines

  1. unit DXInput;
  2.  
  3. interface
  4.  
  5. {$INCLUDE DelphiXcfg.inc}
  6.  
  7. uses
  8.   Windows, Messages, SysUtils, Classes, Controls, Forms, MMSystem,
  9.   DirectX, DXClass;
  10.  
  11. type
  12.  
  13.   {  EDXInputError  }
  14.  
  15.   EDXInputError = class(Exception);
  16.  
  17.   {  EForceFeedbackEffectError  }
  18.  
  19.   EForceFeedbackEffectError = class(Exception);
  20.  
  21.   {  TForceFeedbackEffect  }
  22.  
  23.   TForceFeedbackEffectType = (etNone, etConstantForce, etPeriodic, etCondition);
  24.  
  25.   TForceFeedbackEffect = class;
  26.   TForceFeedbackEffects = class;
  27.  
  28.   TForceFeedbackEffectObject = class
  29.   private
  30.     FAxes: array[0..1] of DWORD;
  31.     FAxesCount: Integer;
  32.     Feff: DIEFFECT;
  33.     FDirections: array[0..1] of DWORD;
  34.     FEnvelope: DIENVELOPE;
  35.     FConstantForce: DICONSTANTFORCE;
  36.     FCondition: DICONDITION;
  37.     FPeriodic: DIPERIODIC;
  38.     FEffect: IDirectInputEffect;
  39.     procedure Clear;
  40.     procedure Init(Effect: TForceFeedbackEffect);
  41.     procedure Release;
  42.   public
  43.     destructor Destroy; override;
  44.   end;
  45.  
  46.   TForceFeedbackEffect = class(TPersistent)
  47.   private
  48.     FRoot: TForceFeedbackEffects;                 
  49.     FParent: TForceFeedbackEffect;
  50.     FList: TList;
  51.     FAttackLevel: Integer;
  52.     FAttackTime: Integer;
  53.     FCondition: TPoint;
  54.     FConstant: TPoint;
  55.     FEffectType: TForceFeedbackEffectType;
  56.     FFadeLevel: Integer;
  57.     FFadeTime: Integer;
  58.     FName: string;
  59.     FPeriod: Integer;
  60.     FPlaying: Boolean;
  61.     FPower: Integer;
  62.     FTime: Integer;
  63.     FObject: TForceFeedbackEffectObject;
  64.     FObject2: TForceFeedbackEffectObject;
  65.     FFindEffectFlag: Boolean;
  66.     FFindEffectGUID: TGUID;
  67.     procedure Acquire;
  68.     procedure Finalize;
  69.     procedure Initialize;
  70.     procedure ChangeEffect;
  71.     procedure MakeEff;
  72.     procedure CreateEffect;
  73.     function GetCount: Integer;
  74.     function GetEffect(Index: Integer): TForceFeedbackEffect;
  75.     function GetIndex: Integer;
  76.     function GetPlaying: Boolean;
  77.     procedure SetAttackLevel(Value: Integer);
  78.     procedure SetAttackTime(Value: Integer);
  79.     procedure SetCondition(Value: TPoint);
  80.     procedure SetConstant(Value: TPoint);
  81.     procedure SetEffectType(Value: TForceFeedbackEffectType);
  82.     procedure SetFadeLevel(Value: Integer);
  83.     procedure SetFadeTime(Value: Integer);
  84.     procedure SetIndex(Value: Integer);
  85.     procedure SetPeriod(Value: Integer);
  86.     procedure SetParent(Value: TForceFeedbackEffect);
  87.     procedure SetPower(Value: Integer);
  88.     procedure SetTime(Value: Integer);
  89.     function HasInterface: Boolean;
  90.   protected
  91.     function GetOwner: TPersistent; override;
  92.   public
  93.     constructor Create(AParent: TForceFeedbackEffect);
  94.     destructor Destroy; override;
  95.     procedure Assign(Source: TPersistent); override;
  96.     procedure Clear;
  97.     function Find(const Name: string): TForceFeedbackEffect;
  98.     function IndexOf(const Name: string): Integer;
  99.     procedure LoadFromFile(const FileName: string);
  100.     procedure LoadFromStream(Stream: TStream);
  101.     procedure SaveToFile(const FileName: string);
  102.     procedure SaveToStream(Stream: TStream);
  103.     procedure Start;
  104.     procedure Stop;
  105.     procedure Unload(Recurse: Boolean);
  106.     property Count: Integer read GetCount;
  107.     property Effects[Index: Integer]: TForceFeedbackEffect read GetEffect; default;
  108.     property Index: Integer read GetIndex write SetIndex;
  109.     property Playing: Boolean read GetPlaying;
  110.     property Parent: TForceFeedbackEffect read FParent write SetParent;
  111.     property Name: string read FName write FName;
  112.     property EffectType: TForceFeedbackEffectType read FEffectType write SetEffectType;
  113.     property AttackLevel: Integer read FAttackLevel write SetAttackLevel;
  114.     property AttackTime: Integer read FAttackTime write SetAttackTime;
  115.     property Condition: TPoint read FCondition write SetCondition;
  116.     property Constant: TPoint read FConstant write SetConstant;
  117.     property FadeLevel: Integer read FFadeLevel write SetFadeLevel;
  118.     property FadeTime: Integer read FFadeTime write SetFadeTime;
  119.     property Period: Integer read FPeriod write SetPeriod;
  120.     property Power: Integer read FPower write SetPower;
  121.     property Time: Integer read FTime write SetTime;
  122.   end;
  123.  
  124.   {  TForceFeedbackEffects  }
  125.  
  126.   TCustomInput = class;
  127.  
  128.   TForceFeedbackEffects = class(TForceFeedbackEffect)
  129.   private
  130.     FComponent: TComponent;
  131.     FInput: TCustomInput;
  132.   protected
  133.     procedure DefineProperties(Filer: TFiler); override;
  134.   public
  135.     constructor Create(Input: TCustomInput);
  136.     destructor Destroy; override;
  137.     property Input: TCustomInput read FInput;
  138.   end;
  139.  
  140.   {  TCustomInput  }
  141.  
  142.   TDXInputState = (isUp, isDown, isLeft, isRight, isButton1, isButton2, isButton3,
  143.     isButton4, isButton5, isButton6, isButton7, isButton8, isButton9, isButton10, isButton11,
  144.     isButton12, isButton13, isButton14, isButton15, isButton16, isButton17, isButton18,
  145.     isButton19, isButton20, isButton21, isButton22, isButton23, isButton24, isButton25,
  146.     isButton26, isButton27, isButton28, isButton29, isButton30, isButton31, isButton32);
  147.  
  148.   TDXInputStates = set of TDXInputState;
  149.  
  150.   TCustomDXInput = class;
  151.  
  152.   TCustomInput = class(TPersistent)
  153.   private
  154.     FBindInputStates: Boolean;
  155.     FButtonCount: Integer;
  156.     FDataFormat: DIDATAFORMAT;
  157.     FDataFormatObjects: array[0..255] of DIOBJECTDATAFORMAT;
  158.     FDataFormatGUIDs: array[0..255] of TGUID;
  159.     FDevice: IDirectInputDevice;
  160.     FDevice2: IDirectInputDevice2;
  161.     FDXInput: TCustomDXInput;
  162.     FEffects: TForceFeedbackEffects;
  163.     FEnabled: Boolean;
  164.     FForceFeedback: Boolean;
  165.     FForceFeedbackDevice: Boolean;
  166.     FInitialized: Boolean;
  167.     FStates: TDXInputStates;
  168.     procedure Acquire;
  169.     procedure Finalize; virtual;
  170.     procedure Initialize; virtual;
  171.     function GetButton(Index: Integer): Boolean;
  172.     function GetCooperativeLevel: Integer; virtual;
  173.     function GetDeviceState(dwSize: Integer; var Data): Boolean;
  174.     function SetDataFormat: Boolean;
  175.     procedure SetEffects(Value: TForceFeedbackEffects);
  176.     procedure SetEnabled(Value: Boolean);
  177.     procedure SetForceFeedback(Value: Boolean);
  178.     procedure SetWindowHandle(Value: Integer);
  179.   public
  180.     constructor Create(DXInput: TCustomDXInput); virtual;
  181.     destructor Destroy; override;
  182.     procedure Update; virtual; abstract;
  183.     property ButtonCount: Integer read FButtonCount;
  184.     property Buttons[Index: Integer]: Boolean read GetButton;
  185.     property States: TDXInputStates read FStates;
  186.   published
  187.     property BindInputStates: Boolean read FBindInputStates write FBindInputStates;
  188.     property Effects: TForceFeedbackEffects read FEffects write SetEffects;
  189.     property Enabled: Boolean read FEnabled write SetEnabled;
  190.     property ForceFeedback: Boolean read FForceFeedback write SetForceFeedback;
  191.   end;
  192.  
  193.   {  TKeyboard  }
  194.  
  195.   PKeyAssign = ^TKeyAssign;
  196.   TKeyAssign = array[0..2] of Integer;
  197.  
  198.   TKeyAssignList = array[TDXInputState] of TKeyAssign;
  199.  
  200.   TKeyboard = class(TCustomInput)
  201.   private
  202.     FKeyStates: TKeyboardState;
  203.     procedure Finalize; override;
  204.     procedure Initialize; override;
  205.     function GetKey(Key: Integer): Boolean;
  206.     procedure ReadAssigns(Stream: TStream);
  207.     procedure WriteAssigns(Stream: TStream);
  208.   protected
  209.     procedure DefineProperties(Filer: TFiler); override;
  210.   public
  211.     KeyAssigns: TKeyAssignList;
  212.     constructor Create(DXInput: TCustomDXInput); override;
  213.     procedure Update; override;
  214.     property Keys[Key: Integer]: Boolean read GetKey;
  215.   end;
  216.  
  217.   {  TMouse  }
  218.  
  219.   TMouse = class(TCustomInput)
  220.   private
  221.     Fdims: DIMOUSESTATE;
  222.     procedure Finalize; override;
  223.     procedure Initialize; override;
  224.     function GetX: Integer;
  225.     function GetY: Integer;
  226.     function GetZ: Integer;
  227.   public
  228.     constructor Create(DXInput: TCustomDXInput); override;
  229.     procedure Update; override;
  230.     property X: Integer read GetX;
  231.     property Y: Integer read GetY;
  232.     property Z: Integer read GetZ;
  233.   end;
  234.  
  235.   {  TJoystick  }
  236.  
  237.   TJoystick = class(TCustomInput)
  238.   private
  239.     Fdijs: DIJOYSTATE2;
  240.     FAutoCenter: Boolean;
  241.     FDeviceGUID: TGUID;
  242.     FEnumFlag: Boolean;
  243.     FEnumIndex: Integer;
  244.     FID: Integer;
  245.     FID2: Integer;
  246.     FJoyCaps: TJoyCaps;
  247.     FDeadZone: array[0..SizeOf(DIJOYSTATE2)-1] of Integer;
  248.     FRange: array[0..SizeOf(DIJOYSTATE2)-1] of Integer;
  249.     procedure Finalize; override;
  250.     procedure Initialize; override;
  251.     function GetCooperativeLevel: Integer; override;
  252.     function GetDeadZone(Obj: Integer): Integer;
  253.     function GetRange(Obj: Integer): Integer;
  254.     function GetX: Integer;
  255.     function GetY: Integer;
  256.     function GetZ: Integer;
  257.     procedure SetDeadZone(Obj: Integer; Value: Integer);
  258.     procedure SetRange(Obj: Integer; Value: Integer);
  259.     procedure SetAutoCenter(Value: Boolean);
  260.     procedure SetID(Value: Integer);
  261.   public
  262.     constructor Create(DXInput: TCustomDXInput); override;
  263.     procedure Update; override;
  264.     property DeadZone[Obj: Integer]: Integer read GetDeadZone write SetDeadZone;
  265.     property Range[Obj: Integer]: Integer read GetRange write SetRange;
  266.     property Joystate: DIJOYSTATE2 read Fdijs;
  267.     property X: Integer read GetX;
  268.     property Y: Integer read GetY;
  269.     property Z: Integer read GetZ;
  270.   published
  271.     property AutoCenter: Boolean read FAutoCenter write SetAutoCenter;
  272.     property DeadZoneX: Integer index DIJOFS_X read GetDeadZone write SetDeadZone;
  273.     property DeadZoneY: Integer index DIJOFS_Y read GetDeadZone write SetDeadZone;
  274.     property DeadZoneZ: Integer index DIJOFS_Z read GetDeadZone write SetDeadZone;
  275.     property ID: Integer read FID write SetID;
  276.     property RangeX: Integer index DIJOFS_X read GetRange write SetRange;
  277.     property RangeY: Integer index DIJOFS_Y read GetRange write SetRange;
  278.     property RangeZ: Integer index DIJOFS_Z read GetRange write SetRange;
  279.   end;
  280.  
  281.   {  TCustomDXInput  }
  282.  
  283.   TCustomDXInput = class(TComponent)
  284.   private
  285.     FActiveOnly: Boolean;
  286.     FDevice: TList;
  287.     FDInput: IDirectInput;
  288.     FForm: TCustomForm;
  289.     FJoystick: TJoystick;
  290.     FKeyboard: TKeyboard;
  291.     FMouse: TMouse;
  292.     FOldStates: TDXInputStates;
  293.     FStates: TDXInputStates;
  294.     FSubClass: TControlSubClass;
  295.     FUseDirectInput: Boolean;
  296.     procedure Finalize;
  297.     procedure Initialize;
  298.     procedure FormWndProc(var Message: TMessage; DefWindowProc: TWndMethod);
  299.     procedure SetActiveOnly(Value: Boolean);
  300.     procedure SetJoystick(Value: TJoystick);
  301.     procedure SetKeyboard(Value: TKeyboard);
  302.     procedure SetMouse(Value: TMouse);
  303.     procedure SetWindowHandle;
  304.     procedure SetUseDirectInput(Value: Boolean);
  305.   protected
  306.     procedure Loaded; override;
  307.   public
  308.     constructor Create(AOwner: TComponent); override;
  309.     destructor Destroy; override;
  310.     procedure Update;
  311.     property ActiveOnly: Boolean read FActiveOnly write SetActiveOnly;
  312.     property Joystick: TJoystick read FJoystick write SetJoystick;
  313.     property Keyboard: TKeyboard read FKeyboard write SetKeyboard;
  314.     property Mouse: TMouse read FMouse write SetMouse;
  315.     property States: TDXInputStates read FStates write FStates;
  316.     property UseDirectInput: Boolean read FUseDirectInput write SetUseDirectInput;
  317.   end;
  318.  
  319.   {  TDXInput  }
  320.  
  321.   TDXInput = class(TCustomDXInput)
  322.   published
  323.     property ActiveOnly;
  324.     property Joystick;
  325.     property Keyboard;
  326.     property Mouse;
  327.     property UseDirectInput;
  328.   end;
  329.  
  330. function DefKeyAssign: TKeyAssignList;
  331. function DefKeyAssign2_1: TKeyAssignList;
  332. function DefKeyAssign2_2: TKeyAssignList;
  333.  
  334. implementation
  335.  
  336. uses DXConsts;
  337.  
  338. procedure AssignKey(var KeyAssignList: TKeyAssignList; State: TDXInputState;
  339.   const Keys: array of Integer);
  340. var
  341.   i, i2: Integer;
  342.   KeyAssign: PKeyAssign;
  343. begin
  344.   KeyAssign := @KeyAssignList[State];
  345.   FillChar(KeyAssign^, SizeOf(TKeyAssign), 0);
  346.  
  347.   i2 := 0;
  348.   for i:=LOW(Keys) to HIGH(Keys) do
  349.   begin
  350.     if i2<3 then
  351.       KeyAssign^[i2] := Keys[i]
  352.     else
  353.       Exit;
  354.     Inc(i2);
  355.   end;
  356. end;
  357.  
  358. function DefKeyAssign: TKeyAssignList;
  359. begin
  360.   FillChar(Result, SizeOf(Result), 0);
  361.  
  362.   AssignKey(Result, isUp,      [Ord('K'), VK_UP, VK_NUMPAD8]);
  363.   AssignKey(Result, isDown,    [Ord('J'), VK_DOWN, VK_NUMPAD2]);
  364.   AssignKey(Result, isLeft,    [Ord('H'), VK_LEFT, VK_NUMPAD4]);
  365.   AssignKey(Result, isRight,   [Ord('L'), VK_RIGHT, VK_NUMPAD6]);
  366.   AssignKey(Result, isButton1, [Ord('Z'), VK_SPACE]);
  367.   AssignKey(Result, isButton2, [Ord('X'), VK_RETURN]);
  368.   AssignKey(Result, isButton9, [VK_F2]);
  369. end;
  370.  
  371. function DefKeyAssign2_1: TKeyAssignList;
  372. begin
  373.   FillChar(Result, SizeOf(Result), 0);
  374.  
  375.   AssignKey(Result, isUp,      [Ord('K'), VK_UP, VK_NUMPAD8]);
  376.   AssignKey(Result, isDown,    [Ord('J'), VK_DOWN, VK_NUMPAD2]);
  377.   AssignKey(Result, isLeft,    [Ord('H'), VK_LEFT, VK_NUMPAD4]);
  378.   AssignKey(Result, isRight,   [Ord('L'), VK_RIGHT, VK_NUMPAD6]);
  379.   AssignKey(Result, isButton1, [VK_SPACE , VK_NUMPAD0]);
  380.   AssignKey(Result, isButton2, [VK_RETURN, VK_NUMPAD5]);
  381.   AssignKey(Result, isButton9, [VK_F2]);
  382. end;
  383.  
  384. function DefKeyAssign2_2: TKeyAssignList;
  385. begin
  386.   FillChar(Result, SizeOf(Result), 0);
  387.  
  388.   AssignKey(Result, isUp,      [Ord('E')]);
  389.   AssignKey(Result, isDown,    [Ord('C')]);
  390.   AssignKey(Result, isLeft,    [Ord('S')]);
  391.   AssignKey(Result, isRight,   [Ord('F')]);
  392.   AssignKey(Result, isButton1, [Ord('Z')]);
  393.   AssignKey(Result, isButton2, [Ord('X')]);
  394.   AssignKey(Result, isButton9, [VK_F2]);
  395. end;
  396.  
  397. {  TForceFeedbackEffectObject  }
  398.  
  399. destructor TForceFeedbackEffectObject.Destroy;
  400. begin
  401.   Release;
  402.   inherited Destroy;
  403. end;
  404.  
  405. function ConvertTime(i: Integer): DWORD;
  406. begin
  407.   if i=-1 then Result := INFINITE else Result := i*1000;
  408. end;
  409.  
  410. procedure TForceFeedbackEffectObject.Clear;
  411. begin
  412.   FillChar(Feff, SizeOf(Feff), 0);
  413. end;
  414.  
  415. procedure TForceFeedbackEffectObject.Init(Effect: TForceFeedbackEffect);
  416. begin
  417.   with FEnvelope do
  418.   begin
  419.     dwSize := SizeOf(FEnvelope);
  420.     dwAttackLevel := Effect.FAttackLevel;
  421.     dwAttackTime := Min(Effect.FAttackTime, Max(Effect.FTime, 0))*1000;
  422.     dwFadeLevel := Effect.FFadeLevel;
  423.     dwFadeTime := Min(Effect.FFadeTime, Max(Effect.FTime, 0))*1000;
  424.   end;
  425.  
  426.   FillChar(Feff, SizeOf(Feff), 0);
  427.   with Feff do
  428.   begin
  429.     dwSize := SizeOf(Feff);
  430.     dwFlags := DIEFF_CARTESIAN or DIEFF_OBJECTOFFSETS;
  431.     dwDuration := ConvertTime(Effect.FTime);
  432.     dwSamplePeriod := 0;
  433.     dwGain := Effect.FPower;
  434.     dwTriggerButton := DIEB_NOTRIGGER;
  435.     dwTriggerRepeatInterval := 0;
  436.     cAxes := FAxesCount;
  437.     rgdwAxes := @FAxes;
  438.     rglDirection := @FDirections;
  439.     lpEnvelope := @FEnvelope;
  440.   end;
  441. end;
  442.  
  443. procedure TForceFeedbackEffectObject.Release;
  444. begin
  445.   FEffect := nil;
  446. end;
  447.  
  448. {  TForceFeedbackEffect  }
  449.  
  450. constructor TForceFeedbackEffect.Create(AParent: TForceFeedbackEffect);
  451. begin
  452.   inherited Create;
  453.   FParent := AParent;
  454.   FList := TList.Create;
  455.  
  456.   if FParent<>nil then
  457.   begin
  458.     FParent.FList.Add(Self);
  459.     FRoot := FParent.FRoot;
  460.   end else
  461.   begin
  462.     FName := 'Effects';
  463.     FRoot := Self as TForceFeedbackEffects;
  464.   end;
  465.  
  466.   FObject := TForceFeedbackEffectObject.Create;
  467.   FObject2 := TForceFeedbackEffectObject.Create;
  468.  
  469.   AttackTime := 0;
  470.   Constant := Point(0, 0);
  471.   EffectType := etNone;
  472.   FadeTime := 0;
  473.   Period := 50;
  474.   Power := 10000;
  475.   Time := 1000;
  476. end;
  477.  
  478. destructor TForceFeedbackEffect.Destroy;
  479. begin
  480.   Clear;
  481.   FObject.Free;
  482.   FObject2.Free;
  483.   FList.Free;
  484.   if FParent<>nil then
  485.     FParent.FList.Remove(Self);
  486.   inherited Destroy;
  487. end;
  488.  
  489. function TForceFeedbackEffect.GetOwner: TPersistent;
  490. begin
  491.   Result := Parent;
  492. end;
  493.  
  494. procedure TForceFeedbackEffect.Assign(Source: TPersistent);
  495. var
  496.   i: Integer;
  497. begin
  498.   if Source is TForceFeedbackEffect then
  499.   begin
  500.     if Source<>Self then
  501.     begin
  502.       Clear;
  503.  
  504.       EffectType := etNone;
  505.  
  506.       Name := TForceFeedbackEffect(Source).Name;
  507.  
  508.       AttackLevel := TForceFeedbackEffect(Source).AttackLevel;
  509.       AttackTime := TForceFeedbackEffect(Source).AttackTime;
  510.       Constant := TForceFeedbackEffect(Source).Constant;
  511.       Condition := TForceFeedbackEffect(Source).Condition;
  512.       EffectType := TForceFeedbackEffect(Source).EffectType;
  513.       FadeLevel := TForceFeedbackEffect(Source).FadeLevel;
  514.       FadeTime := TForceFeedbackEffect(Source).FadeTime;
  515.       Period := TForceFeedbackEffect(Source).Period;
  516.       Power := TForceFeedbackEffect(Source).Power;
  517.       Time := TForceFeedbackEffect(Source).Time;
  518.  
  519.       EffectType := TForceFeedbackEffect(Source).EffectType;
  520.  
  521.       for i:=0 to TForceFeedbackEffect(Source).Count-1 do
  522.         TForceFeedbackEffect.Create(Self).Assign(TForceFeedbackEffect(Source)[i]);
  523.     end;
  524.   end else
  525.     inherited Assign(Source);
  526. end;
  527.  
  528. procedure TForceFeedbackEffect.Acquire;
  529. var
  530.   i: Integer;
  531. begin
  532.   if Playing and (Time=-1) then
  533.     Start;
  534.  
  535.   for i:=0 to Count-1 do
  536.     Effects[i].Initialize;
  537. end;
  538.  
  539. procedure TForceFeedbackEffect.Clear;
  540. begin
  541.   while Count>0 do
  542.     Effects[Count-1].Free;
  543. end;
  544.  
  545. procedure TForceFeedbackEffect.Initialize;
  546. var
  547.   i: Integer;
  548. begin
  549.   CreateEffect;
  550.   for i:=0 to Count-1 do
  551.     Effects[i].Initialize;
  552. end;
  553.  
  554. procedure TForceFeedbackEffect.Finalize;
  555. var
  556.   i: Integer;
  557. begin
  558.   try
  559.     Stop;
  560.     FObject.Release;
  561.     FObject2.Release;
  562.   finally
  563.     for i:=0 to Count-1 do
  564.       Effects[i].Finalize;
  565.   end;
  566. end;
  567.  
  568. function TForceFeedbackEffect.Find(const Name: string): TForceFeedbackEffect;
  569. var
  570.   i, p: Integer;
  571.   Effect: TForceFeedbackEffect;
  572.   AName: string;
  573. begin
  574.   AName := Name;
  575.   Effect := Self;
  576.  
  577.   p := AnsiPos('.', AName);
  578.   while p<>0 do
  579.   begin
  580.     i := Effect.IndexOf(AName);
  581.     if i<>-1 then
  582.     begin
  583.       Result := Effect[i];
  584.       Exit;
  585.     end else
  586.     begin
  587.       i := Effect.IndexOf(Copy(Name, 1, p-1));
  588.       if i=-1 then
  589.         raise EForceFeedbackEffectError.CreateFmt(SEffectNotFound, [Name]);
  590.       Effect := Effect[i];
  591.       AName := Copy(Name, p+1, MaxInt);
  592.       p := AnsiPos('.', AName);
  593.     end;
  594.   end;
  595.  
  596.   i := Effect.IndexOf(Name);
  597.   if i=-1 then
  598.     raise EForceFeedbackEffectError.CreateFmt(SEffectNotFound, [Name]);
  599.   Result := Effect[i];
  600. end;
  601.  
  602. function TForceFeedbackEffect.IndexOf(const Name: string): Integer;
  603. var
  604.   i: Integer;
  605. begin
  606.   Result := -1;
  607.   for i:=0 to Count-1 do
  608.     if Effects[i].Name=Name then
  609.     begin
  610.       Result := i;
  611.       Break;
  612.     end;
  613. end;
  614.  
  615. function TForceFeedbackEffect.HasInterface: Boolean;
  616. begin
  617.   Result := (FEffectType<>etNone) and ((FObject.FEffect<>nil) or (FObject2.FEffect<>nil));
  618. end;
  619.  
  620. procedure TForceFeedbackEffect.MakeEff;
  621. var
  622.   Constant2: TPoint;
  623. begin
  624.   FObject.Clear;
  625.   FObject2.Clear;
  626.  
  627.   with Constant2 do
  628.   begin
  629.     X := -FConstant.X;
  630.     Y := -FConstant.Y;
  631.   end;
  632.  
  633.   case FEffectType of
  634.     etConstantForce:  { etConstantForce }
  635.         begin
  636.           with FObject do
  637.           begin
  638.             FDirections[0] := Constant2.X;
  639.             FDirections[1] := Constant2.Y;
  640.  
  641.             FAxesCount := 2;
  642.             FAxes[0] := DIJOFS_X;
  643.             FAxes[1] := DIJOFS_Y;
  644.  
  645.             with Constant2 do
  646.               FConstantForce.lMagnitude := Trunc(Sqrt(X*X+Y*Y));
  647.  
  648.             Init(Self);
  649.             with Feff do
  650.             begin
  651.               cbTypeSpecificParams := SizeOf(FConstantForce);
  652.               lpvTypeSpecificParams := @FConstantForce;
  653.             end;
  654.           end;
  655.         end;
  656.     etPeriodic:       { etPeriodic }
  657.         begin
  658.           with FObject do
  659.           begin
  660.             FDirections[0] := Constant2.X;
  661.             FDirections[1] := Constant2.Y;
  662.  
  663.             FAxesCount := 2;
  664.             FAxes[0] := DIJOFS_X;
  665.             FAxes[1] := DIJOFS_Y;
  666.  
  667.             with FPeriodic do
  668.             begin
  669.               with Constant2 do
  670.                 dwMagnitude := Trunc(Sqrt(X*X+Y*Y));
  671.               lOffset := 0;
  672.               dwPhase := 0;
  673.               dwPeriod := ConvertTime(FPeriod);
  674.             end;
  675.  
  676.             Init(Self);
  677.             with Feff do
  678.             begin
  679.               cbTypeSpecificParams := SizeOf(FPeriodic);
  680.               lpvTypeSpecificParams := @FPeriodic;
  681.             end;
  682.           end;
  683.         end;
  684.     etCondition:      { etCondition }
  685.         begin
  686.           with FObject do
  687.           begin
  688.             FillChar(FDirections, SizeOf(FDirections), 0);
  689.  
  690.             FAxesCount := 1;
  691.             FAxes[0] := DIJOFS_X;
  692.  
  693.             with FCondition do
  694.             begin
  695.               lOffset := -Constant2.X;
  696.               lPositiveCoefficient := Self.FCondition.X;
  697.               lNegativeCoefficient := -Self.FCondition.X;
  698.               dwPositiveSaturation := 0;
  699.               dwNegativeSaturation := 0;
  700.               lDeadBand := 0;
  701.             end;
  702.  
  703.             Init(Self);
  704.             with Feff do
  705.             begin
  706.               cbTypeSpecificParams := SizeOf(FCondition);
  707.               lpvTypeSpecificParams := @FCondition;
  708.             end;
  709.           end;
  710.  
  711.           with FObject2 do
  712.           begin
  713.             FillChar(FDirections, SizeOf(FDirections), 0);
  714.  
  715.             FAxesCount := 1;
  716.             FAxes[0] := DIJOFS_Y;
  717.  
  718.             with FCondition do
  719.             begin
  720.               lOffset := -Constant2.Y;
  721.               lPositiveCoefficient := Self.FCondition.Y;
  722.               lNegativeCoefficient := -Self.FCondition.Y;
  723.               dwPositiveSaturation := 0;
  724.               dwNegativeSaturation := 0;
  725.               lDeadBand := 0;
  726.             end;
  727.  
  728.             Init(Self);
  729.             with Feff do
  730.             begin
  731.               cbTypeSpecificParams := SizeOf(FCondition);
  732.               lpvTypeSpecificParams := @FCondition;
  733.             end;
  734.           end;
  735.         end;
  736.   end;
  737. end;
  738.  
  739. procedure TForceFeedbackEffect.CreateEffect;
  740.  
  741.   function FindEffectCallBack(const pdei: DIEFFECTINFOA;
  742.     pvRef: Pointer): HRESULT; stdcall;
  743.   begin
  744.     with TForceFeedbackEffect(pvRef) do
  745.     begin
  746.       FFindEffectFlag := True;
  747.       FFindEffectGUID := pdei.guid;
  748.     end;
  749.  
  750.     Result := DIENUM_STOP;
  751.   end;
  752.  
  753.   procedure CreateIEffectGuid(const GUID: TGUID;
  754.     EffectObject: TForceFeedbackEffectObject);
  755.   begin
  756.     if EffectObject.Feff.dwSize=0 then Exit;
  757.  
  758.     if FRoot.FInput.FDevice2<>nil then
  759.       FRoot.FInput.FDevice2.CreateEffect(GUID, EffectObject.Feff, EffectObject.FEffect, nil);
  760.   end;
  761.  
  762.   procedure CreateIEffect(dwFlags: DWORD;
  763.     EffectObject: TForceFeedbackEffectObject);
  764.   begin
  765.     if EffectObject.Feff.dwSize=0 then Exit;
  766.  
  767.     if FRoot.FInput.FDevice2<>nil then
  768.     begin
  769.       FFindEffectFlag := False;
  770.       FRoot.FInput.FDevice2.EnumEffects(@FindEffectCallBack,
  771.         Self, dwFlags);
  772.       if FFindEffectFlag then
  773.         CreateIEffectGuid(FFindEffectGUID, EffectObject);
  774.     end;
  775.   end;
  776.  
  777. begin
  778.   FObject.Release;
  779.   FObject2.Release;
  780.  
  781.   if (FRoot.FInput=nil) or (FRoot.FInput.FDevice2=nil) or
  782.     (not FRoot.FInput.FForceFeedbackDevice) or
  783.     (not FRoot.FInput.FForceFeedback) then Exit;
  784.  
  785.   if FEffectType=etNone then Exit;
  786.  
  787.   MakeEff;
  788.   case FEffectType of
  789.     etConstantForce:
  790.         begin
  791.           CreateIEffectGUID(GUID_ConstantForce, FObject);
  792.         end;
  793.     etPeriodic:
  794.         begin
  795.           CreateIEffect(DIEFT_PERIODIC, FObject);
  796.         end;
  797.     etCondition:
  798.         begin
  799.           CreateIEffect(DIEFT_CONDITION, FObject);
  800.           CreateIEffect(DIEFT_CONDITION, FObject2);
  801.         end;
  802.   end;
  803.  
  804.   if Playing and (Time=-1) then
  805.     Start;
  806. end;
  807.  
  808. procedure TForceFeedbackEffect.ChangeEffect;
  809. var
  810.   dwFlags: DWORD;
  811. begin
  812.   if HasInterface then
  813.   begin
  814.     MakeEff;
  815.  
  816.     dwFlags := DIEP_DIRECTION or DIEP_DURATION or DIEP_ENVELOPE or
  817.       DIEP_GAIN or DIEP_SAMPLEPERIOD or DIEP_TRIGGERBUTTON or
  818.       DIEP_TRIGGERREPEATINTERVAL or DIEP_TYPESPECIFICPARAMS;
  819.  
  820.     if Playing then
  821.       dwFlags := dwFlags or DIEP_START;
  822.  
  823.     if FObject.FEffect<>nil then FObject.FEffect.SetParameters(FObject.Feff, dwFlags);
  824.     if FObject2.FEffect<>nil then FObject2.FEffect.SetParameters(FObject2.Feff, dwFlags);
  825.   end;
  826. end;
  827.  
  828. function TForceFeedbackEffect.GetPlaying: Boolean;
  829. var
  830.   dwFlags: DWORD;
  831. begin
  832.   Result := False;
  833.  
  834.   if not FPlaying then Exit;
  835.  
  836.   if FPlaying and (FTime=-1) then
  837.   begin
  838.     Result := True;
  839.     Exit;
  840.   end;
  841.  
  842.   if FObject.FEffect<>nil then
  843.   begin
  844.     dwFlags := 0;
  845.     FObject.FEffect.GetEffectStatus(dwFlags);
  846.     if dwFlags and DIEGES_PLAYING<>0 then
  847.     begin
  848.       Result := True;
  849.       Exit;
  850.     end;
  851.   end;
  852.  
  853.   if FObject2.FEffect<>nil then
  854.   begin
  855.     dwFlags := 0;
  856.     FObject2.FEffect.GetEffectStatus(dwFlags);
  857.     if dwFlags and DIEGES_PLAYING<>0 then
  858.     begin
  859.       Result := True;
  860.       Exit;
  861.     end;
  862.   end;
  863.  
  864.   if not Result then
  865.     FPlaying := False;
  866. end;
  867.  
  868. function TForceFeedbackEffect.GetCount: Integer;
  869. begin
  870.   Result := FList.Count;
  871. end;
  872.  
  873. function TForceFeedbackEffect.GetEffect(Index: Integer): TForceFeedbackEffect;
  874. begin
  875.   Result :=FList[Index];
  876. end;
  877.  
  878. function TForceFeedbackEffect.GetIndex: Integer;
  879. begin
  880.   if FParent<>nil then
  881.     Result := FParent.FList.IndexOf(Self)
  882.   else
  883.     Result := 0;
  884. end;
  885.  
  886. procedure TForceFeedbackEffect.SetIndex(Value: Integer);
  887. begin
  888.   if FParent<>nil then
  889.   begin
  890.     FParent.FList.Remove(Self);
  891.     FParent.FList.Insert(Value, Self);
  892.   end;
  893. end;
  894.  
  895. procedure TForceFeedbackEffect.SetParent(Value: TForceFeedbackEffect);
  896. begin
  897.   if Parent<>Value then
  898.   begin
  899.     if (Value=nil) or (FRoot<>Value.FRoot) then
  900.       raise EForceFeedbackEffectError.CreateFmt(SCannotChanged, ['Parent']);
  901.  
  902.     FParent.FList.Remove(Self);
  903.     FParent := Value;
  904.     FParent.FList.Add(Self);
  905.   end;
  906. end;
  907.  
  908. procedure TForceFeedbackEffect.SetAttackLevel(Value: Integer);
  909. begin
  910.   if Value<0 then Value := 0;
  911.   if Value>10000 then Value := 10000;
  912.  
  913.   if FAttackLevel<>Value then
  914.   begin
  915.     FAttackLevel := Value;
  916.     ChangeEffect;
  917.   end;
  918. end;
  919.  
  920. procedure TForceFeedbackEffect.SetAttackTime(Value: Integer);
  921. begin
  922.   if Value<0 then Value := 0;
  923.  
  924.   if FAttackTime<>Value then
  925.   begin
  926.     FAttackTime := Value;
  927.     ChangeEffect;
  928.   end;
  929. end;
  930.  
  931. procedure TForceFeedbackEffect.SetCondition(Value: TPoint);
  932. begin
  933.   with Value do
  934.   begin
  935.     if X<-10000 then X := -10000;
  936.     if X>+10000 then X := +10000;
  937.  
  938.     if Y<-10000 then Y := -10000;
  939.     if Y>+10000 then Y := +10000;
  940.   end;
  941.  
  942.   if not CompareMem(@FCondition, @Value, SizeOf(FCondition)) then
  943.   begin
  944.     FCondition := Value;
  945.  
  946.     if HasInterface then
  947.       ChangeEffect;
  948.   end;
  949. end;
  950.  
  951. procedure TForceFeedbackEffect.SetConstant(Value: TPoint);
  952. begin
  953.   with Value do
  954.   begin
  955.     if X<-10000 then X := -10000;
  956.     if X>+10000 then X := +10000;
  957.  
  958.     if Y<-10000 then Y := -10000;
  959.     if Y>+10000 then Y := +10000;
  960.   end;
  961.  
  962.   if not CompareMem(@FConstant, @Value, SizeOf(FConstant)) then
  963.   begin
  964.     FConstant := Value;
  965.  
  966.     if HasInterface then
  967.       ChangeEffect;
  968.   end;
  969. end;
  970.  
  971. procedure TForceFeedbackEffect.SetEffectType(Value: TForceFeedbackEffectType);
  972. begin
  973.   if FEffectType<>Value then
  974.   begin
  975.     FEffectType := Value;
  976.     Stop;
  977.     CreateEffect;
  978.   end;
  979. end;
  980.  
  981. procedure TForceFeedbackEffect.SetFadeLevel(Value: Integer);
  982. begin
  983.   if Value<0 then Value := 0;
  984.   if Value>10000 then Value := 10000;
  985.  
  986.   if FFadeLevel<>Value then
  987.   begin
  988.     FFadeLevel := Value;
  989.     ChangeEffect;
  990.   end;
  991. end;
  992.  
  993. procedure TForceFeedbackEffect.SetFadeTime(Value: Integer);
  994. begin
  995.   if Value<0 then Value := 0;
  996.  
  997.   if FFadeTime<>Value then
  998.   begin
  999.     FFadeTime := Value;
  1000.     ChangeEffect;
  1001.   end;
  1002. end;
  1003.  
  1004. procedure TForceFeedbackEffect.SetPeriod(Value: Integer);
  1005. begin
  1006.   if Value<0 then Value := 0;
  1007.  
  1008.   if FPeriod<>Value then
  1009.   begin
  1010.     FPeriod := Value;
  1011.     ChangeEffect;
  1012.   end;
  1013. end;
  1014.  
  1015. procedure TForceFeedbackEffect.SetPower(Value: Integer);
  1016. begin
  1017.   if Value<0 then Value := 0;
  1018.   if Value>10000 then Value := 10000;
  1019.  
  1020.   if FPower<>Value then
  1021.   begin
  1022.     FPower := Value;
  1023.     ChangeEffect;
  1024.   end;
  1025. end;
  1026.  
  1027. procedure TForceFeedbackEffect.SetTime(Value: Integer);
  1028. begin
  1029.   if (Value<>-1) and (Value<0) then Value := 0;
  1030.  
  1031.   if FTime<>Value then
  1032.   begin
  1033.     FTime := Value;
  1034.     Stop;
  1035.     ChangeEffect;
  1036.   end;
  1037. end;
  1038.  
  1039. procedure TForceFeedbackEffect.Start;
  1040.  
  1041.   procedure StartEffect(Effect: IDirectInputEffect);
  1042.   var
  1043.     hr: HRESULT;
  1044.   begin
  1045.     if Effect<>nil then
  1046.     begin
  1047.       hr := Effect.Start(1, 0);
  1048.       if (hr=DIERR_INPUTLOST) or (hr=DIERR_NOTACQUIRED) then
  1049.       begin
  1050.         FRoot.FInput.Acquire;
  1051.         Effect.Start(1, 0);
  1052.       end;
  1053.     end;
  1054.   end;
  1055.  
  1056. var
  1057.   i: Integer;
  1058. begin
  1059.   for i:=0 to Count-1 do
  1060.     Effects[i].Start;
  1061.  
  1062.   if not HasInterface then
  1063.   begin
  1064.     CreateEffect;
  1065.     if not HasInterface then Exit;
  1066.   end;
  1067.  
  1068.   StartEffect(FObject.FEffect);
  1069.   StartEffect(FObject2.FEffect);
  1070.  
  1071.   FPlaying := True;
  1072. end;
  1073.  
  1074. procedure TForceFeedbackEffect.Stop;
  1075. var
  1076.   i: Integer;
  1077. begin
  1078.   if Playing then
  1079.   begin
  1080.     FPlaying := False;
  1081.     if FObject.FEffect<>nil then FObject.FEffect.Stop;
  1082.     if FObject2.FEffect<>nil then FObject2.FEffect.Stop;
  1083.   end;
  1084.  
  1085.   for i:=0 to Count-1 do
  1086.     Effects[i].Stop;
  1087. end;
  1088.  
  1089. procedure TForceFeedbackEffect.Unload(Recurse: Boolean);
  1090. var
  1091.   i: Integer;
  1092. begin
  1093.   if Playing then
  1094.   begin
  1095.     if FObject.FEffect<>nil then FObject.FEffect.Stop;
  1096.     if FObject2.FEffect<>nil then FObject2.FEffect.Stop;
  1097.   end;
  1098.  
  1099.   if FObject.FEffect<>nil then FObject.FEffect.Unload;
  1100.   if FObject2.FEffect<>nil then FObject2.FEffect.Unload;
  1101.  
  1102.   if Recurse then
  1103.   begin
  1104.     for i:=0 to Count-1 do
  1105.       Effects[i].Unload(True);
  1106.   end;
  1107. end;
  1108.  
  1109. type
  1110.   TForceFeedbackEffectItem = class(TCollectionItem)
  1111.   private
  1112.     FName: string;
  1113.     FEffectType: TForceFeedbackEffectType;
  1114.     FAttackLevel: Integer;
  1115.     FAttackTime: Integer;
  1116.     FConditionX: Integer;
  1117.     FConditionY: Integer;
  1118.     FConstantX: Integer;
  1119.     FConstantY: Integer;
  1120.     FFadeLevel: Integer;
  1121.     FFadeTime: Integer;
  1122.     FPeriod: Integer;
  1123.     FPower: Integer;
  1124.     FTime: Integer;
  1125.     FEffects: TCollection;
  1126.     function GetStoredEffects: Boolean;
  1127.   public
  1128.     constructor Create(Collection: TCollection); override;
  1129.     destructor Destroy; override;
  1130.     procedure Assign(Source: TPersistent); override;
  1131.     procedure AssignTo(Dest: TPersistent); override;
  1132.   published
  1133.     property Name: string read FName write FName;
  1134.     property EffectType: TForceFeedbackEffectType read FEffectType write FEffectType;
  1135.     property AttackLevel: Integer read FAttackLevel write FAttackLevel default 0;
  1136.     property AttackTime: Integer read FAttackTime write FAttackTime default 0;
  1137.     property ConditionX: Integer read FConditionX write FConditionX default 0;
  1138.     property ConditionY: Integer read FConditionY write FConditionY default 0;
  1139.     property ConstantX: Integer read FConstantX write FConstantX default 0;
  1140.     property ConstantY: Integer read FConstantY write FConstantY default 0;
  1141.     property FadeLevel: Integer read FFadeLevel write FFadeLevel default 0;
  1142.     property FadeTime: Integer read FFadeTime write FFadeTime default 0;
  1143.     property Period: Integer read FPeriod write FPeriod;
  1144.     property Power: Integer read FPower write FPower;
  1145.     property Time: Integer read FTime write FTime;
  1146.     property Effects: TCollection read FEffects write FEffects stored GetStoredEffects;
  1147.   end;
  1148.  
  1149.   TForceFeedbackEffectComponent = class(TComponent)
  1150.   private
  1151.     FEffects: TCollection;
  1152.   published
  1153.     property Effects: TCollection read FEffects write FEffects;
  1154.   end;
  1155.  
  1156. constructor TForceFeedbackEffectItem.Create(Collection: TCollection);
  1157. begin
  1158.   inherited Create(Collection);
  1159.   FEffects := TCollection.Create(TForceFeedbackEffectItem);
  1160. end;
  1161.  
  1162. destructor TForceFeedbackEffectItem.Destroy;
  1163. begin
  1164.   FEffects.Free;
  1165.   inherited Destroy;
  1166. end;
  1167.  
  1168. procedure TForceFeedbackEffectItem.Assign(Source: TPersistent);
  1169. var
  1170.   Effect: TForceFeedbackEffect;
  1171.   i: Integer;
  1172. begin
  1173.   Effect := Source as TForceFeedbackEffect;
  1174.  
  1175.   FName := Effect.Name;
  1176.   FEffectType := Effect.EffectType;
  1177.   FAttackLevel := Effect.AttackLevel;
  1178.   FAttackTime := Effect.AttackTime;
  1179.   FConditionX := Effect.Condition.X;
  1180.   FConditionY := Effect.Condition.Y;
  1181.   FConstantX := Effect.Constant.X;
  1182.   FConstantY := Effect.Constant.Y;
  1183.   FFadeLevel := Effect.FadeLevel;
  1184.   FFadeTime := Effect.FadeTime;
  1185.   FPeriod := Effect.Period;
  1186.   FPower := Effect.Power;
  1187.   FTime := Effect.Time;
  1188.  
  1189.   for i:=0 to Effect.Count-1 do
  1190.     TForceFeedbackEffectItem.Create(FEffects).Assign(Effect[i]);
  1191. end;
  1192.  
  1193. procedure TForceFeedbackEffectItem.AssignTo(Dest: TPersistent);
  1194. var
  1195.   Effect: TForceFeedbackEffect;
  1196.   i: Integer;
  1197. begin
  1198.   Effect := Dest as TForceFeedbackEffect;
  1199.  
  1200.   Effect.EffectType := etNone;
  1201.  
  1202.   Effect.Name := FName;
  1203.   Effect.AttackLevel := FAttackLevel;
  1204.   Effect.AttackTime := FAttackTime;
  1205.   Effect.Condition := Point(FConditionX, FConditionY);
  1206.   Effect.Constant := Point(FConstantX, FConstantY);
  1207.   Effect.FadeLevel := FFadeLevel;
  1208.   Effect.FadeTime := FFadeTime;
  1209.   Effect.Period := FPeriod;
  1210.   Effect.Power := FPower;
  1211.   Effect.Time := FTime;
  1212.  
  1213.   Effect.EffectType := FEffectType;
  1214.  
  1215.   for i:=0 to FEffects.Count-1 do
  1216.     TForceFeedbackEffectItem(FEffects.Items[i]).AssignTo(TForceFeedbackEffect.Create(Effect));
  1217. end;
  1218.  
  1219. function TForceFeedbackEffectItem.GetStoredEffects: Boolean;
  1220. begin
  1221.   Result := FEffects.Count>0;
  1222. end;
  1223.  
  1224. procedure TForceFeedbackEffect.LoadFromFile(const FileName: string);
  1225. var
  1226.   Stream: TFileStream;
  1227. begin
  1228.   Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  1229.   try
  1230.     LoadFromStream(Stream);
  1231.   finally
  1232.     Stream.Free;
  1233.   end;
  1234. end;
  1235.  
  1236. procedure TForceFeedbackEffect.LoadFromStream(Stream: TStream);
  1237. var
  1238.   Component: TForceFeedbackEffectComponent;
  1239. begin
  1240.   Clear;
  1241.  
  1242.   Component := TForceFeedbackEffectComponent(FRoot.FComponent);
  1243.   try
  1244.     Component.FEffects := TCollection.Create(TForceFeedbackEffectItem);
  1245.     Stream.ReadComponentRes(Component);
  1246.     TForceFeedbackEffectItem(Component.FEffects.Items[0]).AssignTo(Self);
  1247.   finally
  1248.     Component.FEffects.Free;
  1249.     Component.FEffects := nil;
  1250.   end;
  1251. end;
  1252.  
  1253. procedure TForceFeedbackEffect.SaveToFile(const FileName: string);
  1254. var
  1255.   Stream: TFileStream;
  1256. begin
  1257.   Stream := TFileStream.Create(FileName, fmCreate);
  1258.   try
  1259.     SaveToStream(Stream);
  1260.   finally
  1261.     Stream.Free;
  1262.   end;
  1263. end;
  1264.  
  1265. procedure TForceFeedbackEffect.SaveToStream(Stream: TStream);
  1266. var
  1267.   Component: TForceFeedbackEffectComponent;
  1268. begin
  1269.   Component := TForceFeedbackEffectComponent(FRoot.FComponent);
  1270.   try
  1271.     Component.FEffects := TCollection.Create(TForceFeedbackEffectItem);
  1272.     TForceFeedbackEffectItem.Create(Component.FEffects).Assign(Self);
  1273.     Stream.WriteComponentRes('DelphiXForceFeedbackEffect', Component);
  1274.   finally
  1275.     Component.FEffects.Free;
  1276.     Component.FEffects := nil;
  1277.   end;
  1278. end;
  1279.  
  1280. {  TForceFeedbackEffects  }
  1281.  
  1282. constructor TForceFeedbackEffects.Create(Input: TCustomInput);
  1283. begin
  1284.   inherited Create(nil);
  1285.   FInput := Input;
  1286.   FComponent := TForceFeedbackEffectComponent.Create(nil);
  1287. end;
  1288.  
  1289. destructor TForceFeedbackEffects.Destroy;
  1290. begin
  1291.   FComponent.Free;
  1292.   inherited Destroy;
  1293. end;
  1294.  
  1295. procedure TForceFeedbackEffects.DefineProperties(Filer: TFiler);
  1296. begin
  1297.   inherited DefineProperties(Filer);
  1298.   Filer.DefineBinaryProperty('Effects', LoadFromStream, SaveToStream, True);
  1299. end;
  1300.  
  1301. {  TCustomInput  }
  1302.  
  1303. constructor TCustomInput.Create(DXInput: TCustomDXInput);
  1304. begin
  1305.   inherited Create;
  1306.   FDXInput := DXInput;
  1307.   FDXInput.FDevice.Add(Self);
  1308.   FEffects := TForceFeedbackEffects.Create(Self);
  1309.   FEnabled := True;
  1310.   FBindInputStates := True;
  1311. end;
  1312.  
  1313. destructor TCustomInput.Destroy;
  1314. begin
  1315.   Finalize;
  1316.   FEffects.Free;
  1317.   FDXInput.FDevice.Remove(Self);
  1318.   inherited Destroy;
  1319. end;
  1320.  
  1321. procedure TCustomInput.Acquire;
  1322. begin
  1323.   if FDXInput.FActiveOnly and (GetForegroundWindow<>FDXInput.FForm.Handle) then
  1324.     Exit;
  1325.  
  1326.   if FDevice<>nil then
  1327.     FDevice.Acquire;
  1328.  
  1329.   FEffects.Acquire;
  1330. end;
  1331.  
  1332. procedure TCustomInput.Finalize;
  1333. begin
  1334.   if FDevice<>nil then FDevice.Unacquire;
  1335.   FInitialized := False;
  1336.   FButtonCount := 0;
  1337.   FEffects.Finalize;
  1338.   FDevice := nil;
  1339.   FDevice2 := nil;
  1340.   FForceFeedbackDevice := False;
  1341.   FStates := [];
  1342. end;
  1343.  
  1344. procedure TCustomInput.Initialize;
  1345. begin
  1346.   FInitialized := True;
  1347.   FEffects.Initialize;
  1348. end;
  1349.  
  1350. function TCustomInput.GetButton(Index: Integer): Boolean;
  1351. begin
  1352.   if Index in [0..31] then
  1353.     Result := TDXInputState(Integer(isButton1)+Index) in FStates
  1354.   else
  1355.     Result := False;
  1356. end;
  1357.  
  1358. function TCustomInput.GetCooperativeLevel: Integer;
  1359. const
  1360.   Levels: array[Boolean] of Integer = (DISCL_NONEXCLUSIVE, DISCL_EXCLUSIVE);
  1361.   Levels2: array[Boolean] of Integer = (DISCL_BACKGROUND, DISCL_FOREGROUND);
  1362. begin
  1363.   Result := Levels[FForceFeedbackDevice and FForceFeedback] or Levels2[FDXInput.ActiveOnly];
  1364. end;
  1365.  
  1366. function TCustomInput.GetDeviceState(dwSize: Integer; var Data): Boolean;
  1367. var
  1368.   hr: HRESULT;
  1369. begin
  1370.   FillChar(Data, dwSize, 0);
  1371.  
  1372.   if FDevice<>nil then
  1373.   begin
  1374.     hr := FDevice.GetDeviceState(dwSize, Data);
  1375.     if (hr=DIERR_INPUTLOST) or (hr=DIERR_NOTACQUIRED) then
  1376.     begin
  1377.       FDevice.Acquire;
  1378.       hr := FDevice.GetDeviceState(dwSize, Data);
  1379.     end;
  1380.     Result := hr=DI_OK;
  1381.   end else
  1382.     Result := False;
  1383. end;
  1384.  
  1385. function TCustomInput.SetDataFormat: Boolean;
  1386.  
  1387.   function DIEnumDeviceObjectsProc(const peff: DIDEVICEOBJECTINSTANCEA;
  1388.     pvRef: Pointer): HRESULT; stdcall;
  1389.   begin
  1390.     Result := DIENUM_CONTINUE;
  1391.  
  1392.     with TCustomInput(pvRef) do
  1393.     begin
  1394.       if peff.dwOfs<FDataFormat.dwDataSize then
  1395.       begin
  1396.         FDataFormatGUIDs[FDataFormat.dwNumObjs] := peff.guidType;
  1397.  
  1398.         with FDataFormatObjects[FDataFormat.dwNumObjs] do
  1399.         begin
  1400.           pguid := @FDataFormatGUIDs[FDataFormat.dwNumObjs];
  1401.           dwOfs := peff.dwOfs;
  1402.           dwType := peff.dwType;
  1403.           dwFlags := 0;
  1404.         end;
  1405.         Inc(FDataFormat.dwNumObjs);
  1406.       end;
  1407.     end;
  1408.   end;
  1409.  
  1410. begin
  1411.   Result := False;
  1412.   if FDevice<>nil then
  1413.   begin
  1414.     with FDataFormat do
  1415.     begin
  1416.       dwSize := SizeOf(FDataFormat);
  1417.       dwObjSize := SizeOf(DIOBJECTDATAFORMAT);
  1418.       dwNumObjs := 0;
  1419.       rgodf := @FDataFormatObjects;
  1420.     end;
  1421.  
  1422.     FDevice.EnumObjects(@DIEnumDeviceObjectsProc, Self, DIDFT_ALL);
  1423.     if FDevice.SetDataFormat(FDataFormat)<>DI_OK then Exit;
  1424.   end;
  1425.   Result := True;
  1426. end;
  1427.  
  1428. procedure TCustomInput.SetEffects(Value: TForceFeedbackEffects);
  1429. begin
  1430.   FEffects.Assign(Value);
  1431. end;
  1432.  
  1433. procedure TCustomInput.SetEnabled(Value: Boolean);
  1434. begin
  1435.   if FEnabled<>Value then
  1436.   begin
  1437.     FEnabled := Value;
  1438.     Initialize;
  1439.   end;
  1440. end;
  1441.  
  1442. procedure TCustomInput.SetForceFeedback(Value: Boolean);
  1443. begin
  1444.   if FForceFeedback<>Value then
  1445.   begin
  1446.     FForceFeedback := Value;
  1447.     Initialize;
  1448.   end;
  1449. end;
  1450.  
  1451. procedure TCustomInput.SetWindowHandle(Value: Integer);
  1452. begin
  1453.   if FDevice<>nil then
  1454.     FDevice.SetCooperativeLevel(Value, GetCooperativeLevel);
  1455. end;
  1456.  
  1457. {  TKeyboard  }
  1458.  
  1459. constructor TKeyboard.Create(DXInput: TCustomDXInput);
  1460. begin
  1461.   inherited Create(DXInput);
  1462.   KeyAssigns := DefKeyAssign;
  1463. end;
  1464.  
  1465. procedure TKeyboard.DefineProperties(Filer: TFiler);
  1466. begin
  1467.   inherited DefineProperties(Filer);
  1468.   Filer.DefineBinaryProperty('Aissgns', ReadAssigns, WriteAssigns, False);
  1469.   Filer.DefineBinaryProperty('Assigns', ReadAssigns, WriteAssigns, True);
  1470. end;
  1471.  
  1472. function TKeyboard.GetKey(Key: Integer): Boolean;
  1473. begin
  1474.   if Key in [1..255] then
  1475.     Result := FKeyStates[Key] and $80<>0
  1476.   else
  1477.     Result := False;
  1478. end;
  1479.  
  1480. procedure TKeyboard.Finalize;
  1481. begin
  1482.   FillChar(FKeyStates, SizeOf(FKeyStates), 0);
  1483.   inherited Finalize;
  1484. end;
  1485.  
  1486. procedure TKeyboard.Initialize;
  1487. begin
  1488.   Finalize;
  1489.  
  1490.   if (not FEnabled) or (csDesigning in FDXInput.ComponentState) then Exit;
  1491.  
  1492.   if FDXInput.FDInput<>nil then
  1493.   begin
  1494.     if FDXInput.FDInput.CreateDevice(GUID_SysKeyboard, FDevice, nil)<>DI_OK then Exit;
  1495.     FDevice.SetDataFormat(c_dfDIKeyboard);
  1496.   end;
  1497.  
  1498.   FButtonCount := 32;
  1499.  
  1500.   inherited Initialize;
  1501. end;
  1502.  
  1503. procedure TKeyboard.Update;
  1504.  
  1505.   function DIKEYtoVK(Key: Byte): Integer;
  1506.   begin
  1507.     Result := 0;
  1508.     case Key of
  1509.       DIK_ESCAPE       : Result := VK_ESCAPE;
  1510.       DIK_1            : Result := Ord('1');
  1511.       DIK_2            : Result := Ord('2');
  1512.       DIK_3            : Result := Ord('3');
  1513.       DIK_4            : Result := Ord('4');
  1514.       DIK_5            : Result := Ord('5');
  1515.       DIK_6            : Result := Ord('6');
  1516.       DIK_7            : Result := Ord('7');
  1517.       DIK_8            : Result := Ord('8');
  1518.       DIK_9            : Result := Ord('9');
  1519.       DIK_0            : Result := Ord('0');
  1520.       DIK_EQUALS       : Result := Ord('=');
  1521.       DIK_BACK         : Result := VK_BACK;
  1522.       DIK_TAB          : Result := VK_TAB;
  1523.       DIK_Q            : Result := Ord('Q');
  1524.       DIK_W            : Result := Ord('W');
  1525.       DIK_E            : Result := Ord('E');
  1526.       DIK_R            : Result := Ord('R');
  1527.       DIK_T            : Result := Ord('T');
  1528.       DIK_Y            : Result := Ord('Y');
  1529.       DIK_U            : Result := Ord('U');
  1530.       DIK_I            : Result := Ord('I');
  1531.       DIK_O            : Result := Ord('O');
  1532.       DIK_P            : Result := Ord('P');
  1533.       DIK_LBRACKET     : Result := Ord('[');
  1534.       DIK_RBRACKET     : Result := Ord(']');
  1535.       DIK_RETURN       : Result := VK_RETURN;
  1536.       DIK_LCONTROL     : Result := VK_CONTROL;
  1537.       DIK_A            : Result := Ord('A');
  1538.       DIK_S            : Result := Ord('S');
  1539.       DIK_D            : Result := Ord('D');
  1540.       DIK_F            : Result := Ord('F');
  1541.       DIK_G            : Result := Ord('G');
  1542.       DIK_H            : Result := Ord('H');
  1543.       DIK_J            : Result := Ord('J');
  1544.       DIK_K            : Result := Ord('K');
  1545.       DIK_L            : Result := Ord('L');
  1546.       DIK_SEMICOLON    : Result := Ord(';');
  1547.       DIK_APOSTROPHE   : Result := Ord('''');
  1548.       DIK_LSHIFT       : Result := VK_SHIFT;
  1549.       DIK_BACKSLASH    : Result := Ord('\');
  1550.       DIK_Z            : Result := Ord('Z');
  1551.       DIK_X            : Result := Ord('X');
  1552.       DIK_C            : Result := Ord('C');
  1553.       DIK_V            : Result := Ord('V');
  1554.       DIK_B            : Result := Ord('B');
  1555.       DIK_N            : Result := Ord('N');
  1556.       DIK_M            : Result := Ord('M');
  1557.       DIK_COMMA        : Result := Ord(',');
  1558.       DIK_PERIOD       : Result := Ord('.');
  1559.       DIK_SLASH        : Result := Ord('/');
  1560.       DIK_RSHIFT       : Result := VK_SHIFT;
  1561.       DIK_MULTIPLY     : Result := Ord('*');
  1562.       DIK_LMENU        : Result := VK_MENU;
  1563.       DIK_SPACE        : Result := VK_SPACE;
  1564.       DIK_CAPITAL      : Result := VK_CAPITAL;
  1565.       DIK_F1           : Result := VK_F1;
  1566.       DIK_F2           : Result := VK_F2;
  1567.       DIK_F3           : Result := VK_F3;
  1568.       DIK_F4           : Result := VK_F4;
  1569.       DIK_F5           : Result := VK_F5;
  1570.       DIK_F6           : Result := VK_F6;
  1571.       DIK_F7           : Result := VK_F7;
  1572.       DIK_F8           : Result := VK_F8;
  1573.       DIK_F9           : Result := VK_F9;
  1574.       DIK_F10          : Result := VK_F10;
  1575.       DIK_NUMLOCK      : Result := VK_NUMLOCK;
  1576.       DIK_SCROLL       : Result := VK_SCROLL;
  1577.       DIK_NUMPAD7      : Result := VK_NUMPAD7;
  1578.       DIK_NUMPAD8      : Result := VK_NUMPAD8;
  1579.       DIK_NUMPAD9      : Result := VK_NUMPAD9;
  1580.       DIK_SUBTRACT     : Result := VK_SUBTRACT;
  1581.       DIK_NUMPAD4      : Result := VK_NUMPAD4;
  1582.       DIK_NUMPAD5      : Result := VK_NUMPAD5;
  1583.       DIK_NUMPAD6      : Result := VK_NUMPAD6;
  1584.       DIK_ADD          : Result := VK_ADD;
  1585.       DIK_NUMPAD1      : Result := VK_NUMPAD1;
  1586.       DIK_NUMPAD2      : Result := VK_NUMPAD2;
  1587.       DIK_NUMPAD3      : Result := VK_NUMPAD3;
  1588.       DIK_NUMPAD0      : Result := VK_NUMPAD0;
  1589.       DIK_DECIMAL      : Result := VK_DECIMAL;
  1590.       DIK_F11          : Result := VK_F11;
  1591.       DIK_F12          : Result := VK_F12;
  1592.       DIK_NUMPADENTER  : Result := VK_RETURN;
  1593.       DIK_RCONTROL     : Result := VK_CONTROL;
  1594.       DIK_DIVIDE       : Result := VK_DIVIDE;
  1595.       DIK_RMENU        : Result := VK_MENU;
  1596.       DIK_HOME         : Result := VK_HOME;
  1597.       DIK_UP           : Result := VK_UP;
  1598.       DIK_PRIOR        : Result := VK_PRIOR;
  1599.       DIK_LEFT         : Result := VK_LEFT;
  1600.       DIK_RIGHT        : Result := VK_RIGHT;
  1601.       DIK_END          : Result := VK_END;
  1602.       DIK_DOWN         : Result := VK_DOWN;
  1603.       DIK_NEXT         : Result := VK_NEXT;
  1604.       DIK_INSERT       : Result := VK_INSERT;
  1605.       DIK_DELETE       : Result := VK_DELETE;
  1606.       DIK_LWIN         : Result := VK_LWIN;
  1607.       DIK_RWIN         : Result := VK_RWIN;
  1608.       DIK_APPS         : Result := VK_APPS;
  1609.     end;
  1610.   end;
  1611.  
  1612. var
  1613.   j: Integer;
  1614.   i: TDXInputState;
  1615.   dikb: DIKEYBOARDSTATE;
  1616. begin
  1617.   FillChar(FKeyStates, SizeOf(FKeyStates), 0);
  1618.   FStates := [];
  1619.  
  1620.   if (not FInitialized) or FDXInput.FActiveOnly and (GetForegroundWindow<>FDXInput.FForm.Handle) then
  1621.     Exit;
  1622.  
  1623.   if FDevice<>nil then
  1624.   begin
  1625.     FillChar(dikb, SizeOf(dikb), 0);
  1626.  
  1627.     if GetDeviceState(SizeOf(dikb), dikb) then
  1628.     begin
  1629.       {  The DirectInput key code is converted into the Windows virtual key code.  }
  1630.       for j:=Low(dikb) to High(dikb) do
  1631.         if dikb[j] and $80<>0 then
  1632.           FKeyStates[Byte(DIKEYtoVK(j))] := $80;
  1633.     end;
  1634.   end else
  1635.   begin           
  1636.     GetKeyboardState(FKeyStates);
  1637.   end;
  1638.  
  1639.   for i:=LOW(TDXInputState) to HIGH(TDXInputState) do
  1640.   begin
  1641.     for j:=0 to 2 do
  1642.       if Keys[KeyAssigns[i, j]] then
  1643.       begin
  1644.         FStates := FStates + [i];
  1645.         Break;
  1646.       end;
  1647.   end;
  1648. end;
  1649.  
  1650. procedure TKeyboard.ReadAssigns(Stream: TStream);
  1651. begin
  1652.   Stream.ReadBuffer(KeyAssigns, SizeOf(KeyAssigns));
  1653. end;
  1654.  
  1655. procedure TKeyboard.WriteAssigns(Stream: TStream);
  1656. begin
  1657.   Stream.WriteBuffer(KeyAssigns, SizeOf(KeyAssigns));
  1658. end;
  1659.  
  1660. {  TMouse  }
  1661.  
  1662. constructor TMouse.Create(DXInput: TCustomDXInput);
  1663. begin
  1664.   inherited Create(DXInput);
  1665.   BindInputStates := False;
  1666.   Enabled := False;
  1667. end;               
  1668.  
  1669. function TMouse.GetX: Integer;
  1670. begin
  1671.   Result := Fdims.lX;
  1672. end;
  1673.  
  1674. function TMouse.GetY: Integer;
  1675. begin
  1676.   Result := Fdims.lY;
  1677. end;
  1678.  
  1679. function TMouse.GetZ: Integer;
  1680. begin
  1681.   Result := Fdims.lZ;
  1682. end;
  1683.  
  1684. procedure TMouse.Finalize;
  1685. begin
  1686.   FillChar(Fdims, SizeOf(Fdims), 0);
  1687.   inherited Finalize;
  1688. end;
  1689.  
  1690. procedure TMouse.Initialize;
  1691. begin
  1692.   Finalize;
  1693.  
  1694.   if (not FEnabled) or (csDesigning in FDXInput.ComponentState) then Exit;
  1695.  
  1696.   if FDXInput.FDInput<>nil then
  1697.   begin
  1698.     if FDXInput.FDInput.CreateDevice(GUID_SysMouse, FDevice, nil)<>DI_OK then Exit;
  1699.     FDevice.SetDataFormat(c_dfDIMouse);
  1700.   end else
  1701.     raise EDXInputError.Create(SNecessaryDirectInputUseMouse);
  1702.  
  1703.   FButtonCount := 3;
  1704.  
  1705.   inherited Initialize;
  1706. end;
  1707.  
  1708. procedure TMouse.Update;
  1709. begin
  1710.   FillChar(Fdims, SizeOf(Fdims), 0);
  1711.   FStates := [];
  1712.  
  1713.   if (not FInitialized) or FDXInput.FActiveOnly and (GetForegroundWindow<>FDXInput.FForm.Handle) then
  1714.     Exit;
  1715.  
  1716.   if FDevice<>nil then
  1717.   begin
  1718.     FillChar(Fdims, SizeOf(Fdims), 0);
  1719.     GetDeviceState(SizeOf(Fdims), Fdims);
  1720.   end;
  1721.  
  1722.   if Fdims.lX<0 then FStates := FStates + [isLeft];
  1723.   if Fdims.lX>0 then FStates := FStates + [isRight];
  1724.   if Fdims.lY<0 then FStates := FStates + [isUp];
  1725.   if Fdims.lY>0 then FStates := FStates + [isDown];
  1726.  
  1727.   if Fdims.rgbButtons[0] and $80<>0 then FStates := FStates + [isButton1];
  1728.   if Fdims.rgbButtons[1] and $80<>0 then FStates := FStates + [isButton2];
  1729.   if Fdims.rgbButtons[2] and $80<>0 then FStates := FStates + [isButton3];
  1730. end;
  1731.  
  1732. {  TJoystick  }
  1733.  
  1734. function SetDIDwordProperty(pdev: IDirectInputDevice; guidProperty: PGUID;
  1735.   dwObject, dwHow, dwValue: DWORD): HResult;
  1736. var
  1737.   dipdw: DIPROPDWORD;
  1738. begin
  1739.   dipdw.diph.dwSize       := SizeOf(dipdw);
  1740.   dipdw.diph.dwHeaderSize := SizeOf(dipdw.diph);
  1741.   dipdw.diph.dwObj        := dwObject;
  1742.   dipdw.diph.dwHow        := dwHow;
  1743.   dipdw.dwData            := dwValue;
  1744.  
  1745.   Result := pdev.SetProperty(guidProperty, dipdw.diph);
  1746. end;
  1747.  
  1748. function SetDIRangeProperty(pdev: IDirectInputDevice; guidProperty: PGUID;
  1749.   dwObject, dwHow, Value: DWORD): HResult;
  1750. var
  1751.   diprg: DIPROPRANGE;
  1752. begin
  1753.   diprg.diph.dwSize       := SizeOf(diprg);
  1754.   diprg.diph.dwHeaderSize := SizeOf(diprg.diph);
  1755.   diprg.diph.dwObj        := dwObject;
  1756.   diprg.diph.dwHow        := dwHow;
  1757.   diprg.lMin              := -Value;
  1758.   diprg.lMax              := +Value;
  1759.  
  1760.   Result := pdev.SetProperty(guidProperty, diprg.diph);
  1761. end;
  1762.  
  1763. constructor TJoystick.Create(DXInput: TCustomDXInput);
  1764. begin
  1765.   inherited Create(DXInput);
  1766.   FAutoCenter := True;
  1767.  
  1768.   FID := 0;
  1769.  
  1770.   DeadZoneX := 50;
  1771.   DeadZoneY := 50;
  1772.   DeadZoneZ := 50;
  1773.  
  1774.   RangeX := 1000;
  1775.   RangeY := 1000;
  1776.   RangeZ := 1000;
  1777. end;
  1778.  
  1779. function TJoystick.GetX: Integer;
  1780. begin
  1781.   Result := Fdijs.lX;
  1782. end;
  1783.  
  1784. function TJoystick.GetY: Integer;
  1785. begin
  1786.   Result := Fdijs.lY;
  1787. end;
  1788.  
  1789. function TJoystick.GetZ: Integer;
  1790. begin
  1791.   Result := Fdijs.lZ;
  1792. end;
  1793.  
  1794. procedure TJoystick.Finalize;
  1795. begin
  1796.   FID2 := -1;
  1797.   FillChar(Fdijs, SizeOf(Fdijs), 0);
  1798.   FillChar(FJoyCaps, SizeOf(FJoyCaps), 0);
  1799.   inherited Finalize;
  1800. end;
  1801.  
  1802. function TJoystick.GetCooperativeLevel: Integer;
  1803. begin
  1804.   if not FAutoCenter then
  1805.     Result := DISCL_EXCLUSIVE or DISCL_FOREGROUND
  1806.   else
  1807.     Result := inherited GetCooperativeLevel;
  1808. end;
  1809.  
  1810. function TJoystick_EnumJoysticksCallback(const lpddi: DIDEVICEINSTANCEA;
  1811.   pvRef: Pointer): HRESULT; stdcall;
  1812. begin
  1813.   Result := DIENUM_CONTINUE;
  1814.  
  1815.   with TJoystick(pvRef) do
  1816.   begin
  1817.     if FEnumIndex=FID then
  1818.     begin
  1819.       FDeviceGUID := lpddi.guidInstance;
  1820.       FEnumFlag := True;
  1821.       Result := DIENUM_STOP;
  1822.       Exit;
  1823.     end;
  1824.     Inc(FEnumIndex);
  1825.   end;
  1826. end;
  1827.  
  1828. procedure TJoystick.Initialize;
  1829. var
  1830.   i, j: Integer;
  1831.   devcaps: DIDEVCAPS;
  1832. begin
  1833.   Finalize;
  1834.  
  1835.   if (not FEnabled) or (FID<0) or (csDesigning in FDXInput.ComponentState) then Exit;
  1836.  
  1837.   try
  1838.     try
  1839.       if FDXInput.FDInput<>nil then
  1840.       begin
  1841.         {  Device search.  }
  1842.         FEnumFlag := False;
  1843.         FEnumIndex := 0;
  1844.  
  1845.         FDXInput.FDInput.EnumDevices(DIDEVTYPE_JOYSTICK, @TJoystick_EnumJoysticksCallback,
  1846.           Self, DIEDFL_ATTACHEDONLY);
  1847.  
  1848.         if not FEnumFlag then Exit;
  1849.  
  1850.         {  Device making.  }
  1851.         if FDXInput.FDInput.CreateDevice(FDeviceGUID, FDevice, nil)<>DI_OK then Exit;
  1852.  
  1853.         devcaps.dwSize := SizeOf(devcaps);
  1854.         if FDevice.GetCapabilities(devcaps)=DI_OK then
  1855.         begin
  1856.           FButtonCount := devcaps.dwButtons;
  1857.           if devcaps.dwFlags and DIDC_FORCEFEEDBACK<>0 then
  1858.             FForceFeedbackDevice := True;
  1859.         end;
  1860.  
  1861.         if FDXInput.FDInput.CreateDevice(GUID_Joystick, FDevice, nil)<>DI_OK then Exit;
  1862.  
  1863.         {  Device data format (DIDATAFORMAT) making.  }
  1864.  
  1865.         with FDataFormat do
  1866.         begin
  1867.           dwFlags := DIDF_ABSAXIS;
  1868.           dwDataSize := SizeOf(Fdijs);
  1869.         end;
  1870.  
  1871.         if not SetDataFormat then
  1872.         begin
  1873.           FDevice := nil;
  1874.           Exit;
  1875.         end;
  1876.          
  1877.         AutoCenter := FAutoCenter;
  1878.  
  1879.         for i:=Low(FDeadZone) to High(FDeadZone) do
  1880.           SetDeadZone(i, FDeadZone[i]);
  1881.  
  1882.         for i:=Low(FRange) to High(FRange) do
  1883.           SetRange(i, FRange[i]);
  1884.  
  1885.         FDevice2 := FDevice as IDirectInputDevice2;
  1886.       end;
  1887.     except
  1888.       Finalize;
  1889.       raise;
  1890.     end;
  1891.   finally
  1892.     if FDevice=nil then
  1893.     begin
  1894.       {  Because DirectInput cannot be used,  the GetJoyPosEx function is used.  }
  1895.       FID2 := -1;
  1896.  
  1897.       j := 0;
  1898.       for i:=0 to 255 do
  1899.       begin
  1900.         FillChar(FJoyCaps, SizeOf(FJoyCaps), 0);
  1901.         if joyGetDevCaps(i, @FJoyCaps, SizeOf(FJoyCaps))=JOYERR_NOERROR then
  1902.         begin
  1903.           if FID=j then
  1904.           begin
  1905.             FID2 := i;
  1906.             Break;
  1907.           end;
  1908.           Inc(j);
  1909.         end;
  1910.       end;
  1911.  
  1912.       if FID2<>-1 then
  1913.       begin
  1914.         if joyGetDevCaps(FID2, @FJoyCaps, SizeOf(FJoyCaps))=JOYERR_NOERROR then
  1915.         begin
  1916.           FButtonCount := FJoyCaps.wNumButtons;
  1917.         end else
  1918.         begin
  1919.           FID2 := -1;
  1920.         end;
  1921.       end;
  1922.     end;
  1923.   end;
  1924.  
  1925.   inherited Initialize;
  1926. end;
  1927.  
  1928. procedure TJoystick.SetAutoCenter(Value: Boolean);
  1929. begin
  1930.   FAutoCenter := Value;
  1931.  
  1932.   if FDevice<>nil then
  1933.     SetDIDwordProperty(FDevice, DIPROP_AUTOCENTER, 0, DIPH_DEVICE, Ord(Value));
  1934. end;
  1935.  
  1936. procedure TJoystick.SetID(Value: Integer);
  1937. begin
  1938.   if Value<>FID then
  1939.   begin
  1940.     FID := Value;
  1941.     Initialize;
  1942.   end;
  1943. end;
  1944.  
  1945. function TJoystick.GetDeadZone(Obj: Integer): Integer;
  1946. begin
  1947.   Result := 0;
  1948.   if (Obj>=Low(FDeadZone)) and (Obj<High(FDeadZone)) then
  1949.     Result := FDeadZone[Obj];
  1950. end;
  1951.  
  1952. function TJoystick.GetRange(Obj: Integer): Integer;
  1953. begin
  1954.   Result := 0;
  1955.   if (Obj>=Low(FRange)) and (Obj<High(FRange)) then
  1956.     Result := FRange[Obj];
  1957. end;
  1958.  
  1959. procedure TJoystick.SetDeadZone(Obj: Integer; Value: Integer);
  1960. begin
  1961.   if (Obj<Low(FDeadZone)) or (Obj>=High(FDeadZone)) then Exit;
  1962.  
  1963.   if Value<0 then Value := 0;
  1964.   if Value>100 then Value := 100;
  1965.  
  1966.   if Obj=Integer(@PDIJOYSTATE2(nil).rgdwPOV[0]) then
  1967.   begin
  1968.     FDeadZone[Obj] := -1;
  1969.     Exit;
  1970.   end;
  1971.  
  1972.   FDeadZone[Obj] := Value;
  1973.  
  1974.   if FDevice<>nil then
  1975.   begin
  1976.     if SetDIDwordProperty(FDevice, DIPROP_DEADZONE, Obj, DIPH_BYOFFSET, Value*100)<>DI_OK then
  1977.       FDeadZone[Obj] := -1;
  1978.   end;
  1979. end;
  1980.  
  1981. procedure TJoystick.SetRange(Obj: Integer; Value: Integer);
  1982. begin
  1983.   if (Obj<Low(FRange)) or (Obj>=High(FRange)) then Exit;
  1984.  
  1985.   if Value<0 then Value := 0;
  1986.  
  1987.   if Obj=Integer(@PDIJOYSTATE2(nil).rgdwPOV[0]) then
  1988.   begin
  1989.     FRange[Obj] := -1;
  1990.     Exit;
  1991.   end;
  1992.  
  1993.   FRange[Obj] := Value;
  1994.  
  1995.   if FDevice<>nil then
  1996.   begin
  1997.     if SetDIRangeProperty(FDevice, DIPROP_RANGE, Obj, DIPH_BYOFFSET, Value)<>DI_OK then
  1998.       FRange[Obj] := -1;
  1999.   end;
  2000. end;
  2001.  
  2002. procedure TJoystick.Update;
  2003.  
  2004.   function ConvertValue(Value, wXmax, wXmin, DeadZone, Range: Integer): Integer;
  2005.   var
  2006.     c, w: Integer;
  2007.   begin
  2008.     Result := 0;
  2009.  
  2010.     c := (wXmax - wXmin) div 2;
  2011.     Value := Value-c;
  2012.  
  2013.     w := c*DeadZone div 100;
  2014.     c := c - w;
  2015.  
  2016.     if c=0 then Exit;
  2017.  
  2018.     if Abs(Value)>w then
  2019.     begin
  2020.       if Value>0 then
  2021.         Result := MulDiv(Value-w, Range, c)
  2022.       else
  2023.         Result := MulDiv(Value+w, Range, c);
  2024.     end;
  2025.   end;
  2026.  
  2027. var
  2028.   i: Integer;
  2029.   JoyInfo: TJoyInfoEx;
  2030. begin
  2031.   FillChar(Fdijs, SizeOf(Fdijs), 0);
  2032.   FStates := [];
  2033.  
  2034.   if (not FInitialized) or FDXInput.FActiveOnly and (GetForegroundWindow<>FDXInput.FForm.Handle) then
  2035.     Exit;
  2036.  
  2037.   if FDevice<>nil then
  2038.   begin
  2039.     FDevice2.Poll;
  2040.     GetDeviceState(SizeOf(Fdijs), Fdijs);
  2041.   end else
  2042.   begin
  2043.     if FID2<>-1 then
  2044.     begin
  2045.       JoyInfo.dwSize := SizeOf(JoyInfo);
  2046.       JoyInfo.dwFlags := JOY_RETURNX or JOY_RETURNY or JOY_RETURNZ or JOY_RETURNPOV or
  2047.         JOY_RETURNBUTTONS or JOY_RETURNCENTERED;
  2048.  
  2049.       joyGetPosEx(FID2, @JoyInfo);
  2050.  
  2051.       with FJoyCaps do
  2052.         Fdijs.lX := ConvertValue(JoyInfo.wXpos, wXmax, wXmin, FDeadZone[DIJOFS_X], FRange[DIJOFS_X]);
  2053.  
  2054.       with FJoyCaps do
  2055.         Fdijs.lY := ConvertValue(JoyInfo.wYpos, wYmax, wYmin, FDeadZone[DIJOFS_Y], FRange[DIJOFS_Y]);
  2056.  
  2057.       with FJoyCaps do
  2058.         Fdijs.lZ := ConvertValue(JoyInfo.wZpos, wZmax, wZmin, FDeadZone[DIJOFS_Z], FRange[DIJOFS_Z]);
  2059.  
  2060.       Fdijs.rgdwPOV[0] := JoyInfo.dwPOV;
  2061.  
  2062.       for i:=0 to FJoyCaps.wNumButtons-1 do
  2063.         if JoyInfo.wButtons and (1 shl i)<>0 then
  2064.           Fdijs.rgbButtons[i] := $80;
  2065.     end;
  2066.   end;
  2067.  
  2068.   for i:=0 to 31 do
  2069.     if Fdijs.rgbButtons[i] and $80<>0 then
  2070.       FStates := FStates + [TDXInputState(Ord(isButton1)+i)];
  2071.  
  2072.   if Fdijs.lX<0 then FStates := FStates + [isLeft];
  2073.   if Fdijs.lX>0 then FStates := FStates + [isRight];
  2074.   if Fdijs.lY<0 then FStates := FStates + [isUp];
  2075.   if Fdijs.lY>0 then FStates := FStates + [isDown];
  2076. end;
  2077.  
  2078. {  TCustomDXInput  }
  2079.  
  2080. var
  2081.   FDirectInput: IDirectInput;
  2082.   FDirectInputCount: Integer;
  2083.  
  2084. procedure InitDirectInput(out DI: IDirectInput);
  2085. type
  2086.   TDirectInputCreate = function(hinst: THandle; dwVersion: DWORD;
  2087.     out ppDI: IDirectInputA; punkOuter: IUnknown): HRESULT; stdcall;
  2088. begin
  2089.   if FDirectInput=nil then
  2090.   begin
  2091.     try
  2092.       TDirectInputCreate(DXLoadLibrary('DInput.dll', 'DirectInputCreateA'))
  2093.         (HInstance, DIRECTINPUT_VERSION, FDirectInput, nil);
  2094.     except
  2095.       FDirectInput := nil;
  2096.     end;
  2097.   end;
  2098.  
  2099.   DI := FDirectInput;
  2100.   if FDirectInput<>nil then
  2101.     Inc(FDirectInputCount);
  2102. end;
  2103.  
  2104. procedure FinDirectInput(var DI: IDirectInput);
  2105. begin
  2106.   if DI<>nil then
  2107.   begin
  2108.     DI := nil;
  2109.     Dec(FDirectInputCount);
  2110.     if FDirectInputCount<=0 then
  2111.     begin
  2112.       FDirectInputCount := 0;
  2113.       FDirectInput := nil;
  2114.     end;
  2115.   end;
  2116. end;
  2117.  
  2118. constructor TCustomDXInput.Create(AOwner: TComponent);
  2119. var
  2120.   Component: TComponent;
  2121. begin
  2122.   inherited Create(AOwner);
  2123.  
  2124.   FDevice := TList.Create;
  2125.  
  2126.   FActiveOnly := True;
  2127.   FJoystick := TJoystick.Create(Self);
  2128.   FKeyboard := TKeyboard.Create(Self);
  2129.   FMouse := TMouse.Create(Self);
  2130.   FUseDirectInput := True;
  2131.  
  2132.   Component := Owner;
  2133.   while (Component<>nil) and (not (Component is TCustomForm)) do
  2134.     Component := Component.Owner;
  2135.   if Component=nil then
  2136.     raise EDXInputError.CreateFmt(SNoForm, ['Owner']);
  2137.   FForm := TCustomForm(Component);
  2138.  
  2139.   FSubClass := TControlSubClass.Create(FForm, FormWndProc);
  2140. end;
  2141.  
  2142. destructor TCustomDXInput.Destroy;
  2143. begin
  2144.   Finalize;
  2145.   FJoystick.Free;
  2146.   FKeyboard.Free;
  2147.   FMouse.Free;
  2148.   FSubClass.Free;
  2149.   while FDevice.Count>0 do
  2150.     TCustomInput(FDevice[FDevice.Count-1]).Free;
  2151.   FDevice.Free;
  2152.   inherited Destroy;
  2153. end;
  2154.  
  2155. procedure TCustomDXInput.FormWndProc(var Message: TMessage; DefWindowProc: TWndMethod);
  2156.  
  2157.   procedure AcquireDevice;
  2158.   var
  2159.     i: Integer;
  2160.   begin
  2161.     for i:=0 to FDevice.Count-1 do
  2162.       TCustomInput(FDevice[i]).Acquire;
  2163.   end;
  2164.  
  2165. begin
  2166.   case Message.Msg of
  2167.     WM_CREATE:
  2168.         begin
  2169.           {  Window handle of Form changed.  }
  2170.           DefWindowProc(Message);
  2171.           SetWindowHandle;
  2172.           Exit;
  2173.         end;
  2174.     WM_ACTIVATEAPP:
  2175.         begin
  2176.           DefWindowProc(Message);
  2177.           if TWMActivateApp(Message).Active then
  2178.             AcquireDevice;
  2179.           Exit;
  2180.         end;
  2181.     WM_ACTIVATE:
  2182.         begin
  2183.           DefWindowProc(Message);
  2184.           if TWMActivate(Message).Active<>WA_INACTIVE then
  2185.             AcquireDevice;
  2186.           Exit;
  2187.         end;
  2188.   end;
  2189.   DefWindowProc(Message);
  2190. end;
  2191.  
  2192. procedure TCustomDXInput.Finalize;
  2193. var
  2194.   i: Integer;
  2195. begin
  2196.   for i:=0 to FDevice.Count-1 do
  2197.     TCustomInput(FDevice[i]).Finalize;
  2198.   FinDirectInput(FDInput);
  2199. end;
  2200.  
  2201. procedure TCustomDXInput.Loaded;
  2202. begin
  2203.   Initialize;
  2204. end;
  2205.  
  2206. procedure TCustomDXInput.Initialize;
  2207. var
  2208.   i: Integer;
  2209. begin
  2210.   Finalize;
  2211.   if not (csDesigning in ComponentState) then
  2212.   begin
  2213.     if FUseDirectInput then InitDirectInput(FDInput);
  2214.  
  2215.     for i:=0 to FDevice.Count-1 do
  2216.       TCustomInput(FDevice[i]).Initialize;
  2217.  
  2218.     SetWindowHandle;
  2219.  
  2220.     Update;
  2221.   end;
  2222. end;
  2223.  
  2224. procedure TCustomDXInput.SetActiveOnly(Value: Boolean);
  2225. begin
  2226.   if Value<>FActiveOnly then
  2227.   begin
  2228.     FActiveOnly := Value;
  2229.     if [csLoading, csReading]*ComponentState=[] then SetWindowHandle;
  2230.   end;
  2231. end;
  2232.  
  2233. procedure TCustomDXInput.SetJoystick(Value: TJoystick);
  2234. begin
  2235.   FJoystick.Assign(Value);
  2236. end;
  2237.  
  2238. procedure TCustomDXInput.SetKeyboard(Value: TKeyboard);
  2239. begin
  2240.   FKeyboard.Assign(Value);
  2241. end;
  2242.  
  2243. procedure TCustomDXInput.SetMouse(Value: TMouse);
  2244. begin
  2245.   FMouse.Assign(Value);
  2246. end;
  2247.  
  2248. procedure TCustomDXInput.SetUseDirectInput(Value: Boolean);
  2249. begin
  2250.   if FUseDirectInput<>Value then
  2251.   begin
  2252.     FUseDirectInput := Value;
  2253.     Initialize;
  2254.   end;
  2255. end;
  2256.  
  2257. procedure TCustomDXInput.SetWindowHandle;
  2258. var
  2259.   i: Integer;
  2260. begin
  2261.   for i:=0 to FDevice.Count-1 do
  2262.     TCustomInput(FDevice[i]).SetWindowHandle(FForm.Handle);
  2263. end;
  2264.  
  2265. procedure TCustomDXInput.Update;
  2266. var
  2267.   j: Integer;
  2268.   i: TDXInputState;
  2269.   s: TDXInputStates;
  2270. begin
  2271.   s := [];
  2272.  
  2273.   for j:=0 to FDevice.Count-1 do
  2274.   begin
  2275.     TCustomInput(FDevice[j]).Update;
  2276.     if TCustomInput(FDevice[j]).FBindInputStates then
  2277.       s := s + TCustomInput(FDevice[j]).States;
  2278.   end;
  2279.  
  2280.   for i:=Low(TDXInputState) to High(TDXInputState) do
  2281.   begin
  2282.     if (i in s) and (not (i in FOldStates)) then
  2283.       FStates := FStates + [i];
  2284.     if (not (i in s)) and (i in FOldStates) then
  2285.       FStates := FStates - [i];
  2286.   end;
  2287.  
  2288.   FOldStates := s;
  2289. end;
  2290.  
  2291. end.
  2292.