1
0
mirror of synced 2024-11-16 15:33:50 +00:00

Added: automatic detection of Throttle plug in / out

Added: actual SimConnect connection and auto-reconnect
This commit is contained in:
Mark van Renswoude 2013-02-21 21:59:29 +00:00
parent f1b9a63fda
commit 4d0e2a5af6
15 changed files with 801 additions and 334 deletions

View File

@ -101,7 +101,6 @@ object ButtonFunctionForm: TButtonFunctionForm
OnFocusChanged = vstFunctionsFocusChanged OnFocusChanged = vstFunctionsFocusChanged
OnGetText = vstFunctionsGetText OnGetText = vstFunctionsGetText
OnPaintText = vstFunctionsPaintText OnPaintText = vstFunctionsPaintText
ExplicitHeight = 383
Columns = < Columns = <
item item
Position = 0 Position = 0
@ -122,7 +121,6 @@ object ButtonFunctionForm: TButtonFunctionForm
Align = alClient Align = alClient
BevelOuter = bvNone BevelOuter = bvNone
TabOrder = 1 TabOrder = 1
ExplicitHeight = 383
object pnlName: TPanel object pnlName: TPanel
Left = 0 Left = 0
Top = 0 Top = 0
@ -194,7 +192,6 @@ object ButtonFunctionForm: TButtonFunctionForm
Align = alClient Align = alClient
BorderStyle = bsNone BorderStyle = bsNone
TabOrder = 1 TabOrder = 1
ExplicitHeight = 286
end end
end end
object pnlHeader: TPanel object pnlHeader: TPanel

View File

@ -469,7 +469,7 @@ begin
FreeAndNil(FComboBox); FreeAndNil(FComboBox);
FreeAndNil(FStateLabel); FreeAndNil(FStateLabel);
inherited; inherited Destroy;
end; end;
end. end.

View File

@ -356,6 +356,10 @@ object MainForm: TMainForm
object tsAbout: TTabSheet object tsAbout: TTabSheet
Caption = 'About' Caption = 'About'
ImageIndex = 1 ImageIndex = 1
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object lblVersionCaption: TLabel object lblVersionCaption: TLabel
Left = 16 Left = 16
Top = 67 Top = 67
@ -842,16 +846,5 @@ object MainForm: TMainForm
ParentFont = False ParentFont = False
ExplicitWidth = 401 ExplicitWidth = 401
end end
object btnRetry: TButton
Left = 374
Top = 20
Width = 75
Height = 25
Anchors = [akTop, akRight]
Caption = '&Retry'
TabOrder = 0
Visible = False
OnClick = btnRetryClick
end
end end
end end

View File

@ -2,15 +2,15 @@ unit MainFrm;
interface interface
uses uses
Classes, System.Classes,
Contnrs, System.Contnrs,
Controls, Vcl.ComCtrls,
ComCtrls, Vcl.Controls,
ExtCtrls, Vcl.ExtCtrls,
Forms, Vcl.Forms,
Messages, Vcl.StdCtrls,
StdCtrls, Winapi.Messages,
Windows, Winapi.Windows,
OtlComm, OtlComm,
OtlEventMonitor, OtlEventMonitor,
@ -20,7 +20,7 @@ uses
X2UtPersistIntf, X2UtPersistIntf,
LEDStateConsumer, LEDStateConsumer,
Profile; Profile, Vcl.AppEvnts;
const const
@ -31,6 +31,12 @@ const
LED_COUNT = 8; LED_COUNT = 8;
DBT_DEVICEARRIVAL = $8000;
DBT_DEVICEREMOVECOMPLETE = $8004;
DBT_DEVTYP_DEVICEINTERFACE = $0005;
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = $0004;
type type
TLEDControls = record TLEDControls = record
ConfigureButton: TButton; ConfigureButton: TButton;
@ -44,7 +50,6 @@ type
lblG940Throttle: TLabel; lblG940Throttle: TLabel;
imgStateFound: TImage; imgStateFound: TImage;
lblG940ThrottleState: TLabel; lblG940ThrottleState: TLabel;
btnRetry: TButton;
PageControl: TPageControl; PageControl: TPageControl;
pnlG940: TPanel; pnlG940: TPanel;
tsAbout: TTabSheet; tsAbout: TTabSheet;
@ -91,7 +96,6 @@ type
bvlProfiles: TBevel; bvlProfiles: TBevel;
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure btnRetryClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
procedure btnCheckUpdatesClick(Sender: TObject); procedure btnCheckUpdatesClick(Sender: TObject);
@ -107,6 +111,14 @@ type
FActiveProfile: TProfile; FActiveProfile: TProfile;
FLoadingProfiles: Boolean; FLoadingProfiles: Boolean;
FStateConsumerTask: IOmniTaskControl; FStateConsumerTask: IOmniTaskControl;
FDeviceNotification: Pointer;
FG940Found: Boolean;
protected
procedure RegisterDeviceArrival;
procedure UnregisterDeviceArrival;
procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
protected protected
procedure FindLEDControls; procedure FindLEDControls;
procedure LoadProfiles; procedure LoadProfiles;
@ -126,9 +138,6 @@ type
procedure EventMonitorTerminated(const task: IOmniTaskControl); procedure EventMonitorTerminated(const task: IOmniTaskControl);
procedure HandleDeviceStateMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage); procedure HandleDeviceStateMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage);
procedure HandleRunInMainThreadMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage);
procedure HandleProviderKilled(ATask: IOmniTaskControl; AMessage: TOmniMessage);
procedure HandleProviderKilledFSX(ATask: IOmniTaskControl; AMessage: TOmniMessage);
procedure CMAskAutoUpdate(var Msg: TMessage); message CM_ASKAUTOUPDATE; procedure CMAskAutoUpdate(var Msg: TMessage); message CM_ASKAUTOUPDATE;
@ -204,9 +213,10 @@ begin
FProfiles := TProfileList.Create(True); FProfiles := TProfileList.Create(True);
LoadProfiles; LoadProfiles;
// TODO implement profile changing properly
FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile);
// LoadFunctions(TFSXLEDStateProvider, FFSXComboBoxes);
// LoadDefaultProfile; RegisterDeviceArrival;
end; end;
@ -225,10 +235,63 @@ end;
procedure TMainForm.FormDestroy(Sender: TObject); procedure TMainForm.FormDestroy(Sender: TObject);
begin begin
UnregisterDeviceArrival;
FreeAndNil(FProfiles); FreeAndNil(FProfiles);
end; end;
procedure TMainForm.RegisterDeviceArrival;
type
TDevBroadcastDeviceInterface = packed record
dbcc_size: DWORD;
dbcc_devicetype: DWORD;
dbcc_reserved: DWORD;
dbcc_classguid: TGUID;
dbcc_name: PChar;
end;
var
request: TDevBroadcastDeviceInterface;
begin
ZeroMemory(@request, SizeOf(request));
request.dbcc_size := SizeOf(request);
request.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE;
FDeviceNotification := RegisterDeviceNotification(Self.Handle, @request,
DEVICE_NOTIFY_WINDOW_HANDLE or
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
end;
procedure TMainForm.UnregisterDeviceArrival;
begin
if Assigned(FDeviceNotification) then
begin
UnregisterDeviceNotification(FDeviceNotification);
FDeviceNotification := nil;
end;
end;
procedure TMainForm.WMDeviceChange(var Msg: TMessage);
begin
if not Assigned(StateConsumerTask) then
exit;
case Msg.WParam of
DBT_DEVICEARRIVAL:
if (not FG940Found) then
StateConsumerTask.Comm.Send(TM_FINDTHROTTLEDEVICE);
DBT_DEVICEREMOVECOMPLETE:
if FG940Found then
StateConsumerTask.Comm.Send(TM_TESTTHROTTLEDEVICE);
end;
end;
procedure TMainForm.FindLEDControls; procedure TMainForm.FindLEDControls;
function ComponentByName(const AName: string; ATag: NativeInt): TComponent; function ComponentByName(const AName: string; ATag: NativeInt): TComponent;
@ -417,36 +480,11 @@ begin
imgStateFound.Visible := AFound; imgStateFound.Visible := AFound;
imgStateNotFound.Visible := not AFound; imgStateNotFound.Visible := not AFound;
FG940Found := AFound;
end; end;
//procedure TMainForm.ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray);
//var
// comboBox: TComboBoxEx;
// value: Integer;
// itemIndex: Integer;
//
//begin
// if AReader.BeginSection(SECTION_FSX) then
// try
// for comboBox in AComboBoxes do
// begin
// if AReader.ReadInteger('Function' + IntToStr(comboBox.Tag), value) then
// begin
// for itemIndex := 0 to Pred(comboBox.ItemsEx.Count) do
// if Integer(comboBox.ItemsEx[itemIndex].Data) = value then
// begin
// comboBox.ItemIndex := itemIndex;
// break;
// end;
// end;
// end;
// finally
// AReader.EndSection;
// end;
//end;
//procedure TMainForm.ReadAutoUpdate(AReader: IX2PersistReader); //procedure TMainForm.ReadAutoUpdate(AReader: IX2PersistReader);
//var //var
// checkUpdates: Boolean; // checkUpdates: Boolean;
@ -484,29 +522,6 @@ end;
//end; //end;
//procedure TMainForm.InitializeStateProvider(AProviderClass: TLEDStateProviderClass);
//begin
// UpdateMapping;
// LEDStateConsumer.InitializeStateProvider(StateConsumerTask, AProviderClass);
//end;
//
//
//procedure TMainForm.FinalizeStateProvider;
//begin
// LEDStateConsumer.FinalizeStateProvider(StateConsumerTask);
//end;
//procedure TMainForm.UpdateMapping;
//begin
// if not Assigned(StateConsumerTask) then
// Exit;
//
// LEDStateConsumer.ClearFunctions(StateConsumerTask);
// SetFunctions(FFSXComboBoxes);
//end;
procedure TMainForm.LEDButtonClick(Sender: TObject); procedure TMainForm.LEDButtonClick(Sender: TObject);
var var
buttonIndex: NativeInt; buttonIndex: NativeInt;
@ -628,32 +643,31 @@ end;
procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage); procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin begin
case msg.MsgID of case msg.MsgID of
TM_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg); TM_NOTIFY_DEVICESTATE:
// MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg); HandleDeviceStateMessage(task, msg);
// MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg);
MSG_UPDATE: MSG_UPDATE:
if MessageBox(Self.Handle, 'An update is available on the G940 LED Control website.'#13#10'Do you want to go there now?', if MessageBox(Self.Handle, 'An update is available on the G940 LED Control website.'#13#10'Do you want to go there now?',
'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then 'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then
ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL); ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL);
// MSG_NOUPDATE: MSG_NOUPDATE:
// if msg.MsgData.AsBoolean then if msg.MsgData.AsBoolean then
// MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION) MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION)
// else else
// MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING); MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING);
end; end;
end; end;
procedure TMainForm.EventMonitorTerminated(const task: IOmniTaskControl); procedure TMainForm.EventMonitorTerminated(const task: IOmniTaskControl);
begin begin
// if task = StateConsumerTask then if task = StateConsumerTask then
// begin begin
// FStateConsumerTask := nil; FStateConsumerTask := nil;
// Close; Close;
// end else if task.Name = 'CheckForUpdatesThread' then end else if task.Name = 'CheckForUpdatesThread' then
// btnCheckUpdates.Enabled := True; btnCheckUpdates.Enabled := True;
end; end;
@ -667,43 +681,9 @@ begin
SetDeviceState(TEXT_STATE_FOUND, True); SetDeviceState(TEXT_STATE_FOUND, True);
DEVICESTATE_NOTFOUND: DEVICESTATE_NOTFOUND:
begin
SetDeviceState(TEXT_STATE_NOTFOUND, False); SetDeviceState(TEXT_STATE_NOTFOUND, False);
btnRetry.Visible := True;
end; end;
end; end;
end;
procedure TMainForm.HandleRunInMainThreadMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage);
var
executor: IRunInMainThread;
begin
executor := (AMessage.MsgData.AsInterface as IRunInMainThread);
executor.Execute;
executor.Signal;
end;
procedure TMainForm.HandleProviderKilled(ATask: IOmniTaskControl; AMessage: TOmniMessage);
begin
HandleProviderKilledFSX(ATask, AMessage);
end;
procedure TMainForm.HandleProviderKilledFSX(ATask: IOmniTaskControl; AMessage: TOmniMessage);
var
msg: string;
begin
// btnFSXDisconnect.Enabled := False;
// btnFSXConnect.Enabled := True;
msg := AMessage.MsgData;
if Length(msg) > 0 then
ShowMessage(msg);
end;
procedure TMainForm.btnCheckUpdatesClick(Sender: TObject); procedure TMainForm.btnCheckUpdatesClick(Sender: TObject);
@ -712,13 +692,6 @@ begin
end; end;
procedure TMainForm.btnRetryClick(Sender: TObject);
begin
btnRetry.Visible := False;
// StateConsumerTask.Comm.Send(MSG_FINDTHROTTLEDEVICE);
end;
procedure TMainForm.lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); procedure TMainForm.lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
begin begin
ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL); ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL);

View File

@ -98,7 +98,6 @@
<DCC_UNIT_PLATFORM>False</DCC_UNIT_PLATFORM> <DCC_UNIT_PLATFORM>False</DCC_UNIT_PLATFORM>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''"> <PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
<DCC_DebugDCUs>true</DCC_DebugDCUs>
<VerInfo_IncludeVerInfo>false</VerInfo_IncludeVerInfo> <VerInfo_IncludeVerInfo>false</VerInfo_IncludeVerInfo>
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=0.2.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=0.2;Comments=</VerInfo_Keys> <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=0.2.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=0.2;Comments=</VerInfo_Keys>
<Manifest_File>F:\Components\X2Utils\Resources\VistaManAsInvoker.manifest</Manifest_File> <Manifest_File>F:\Components\X2Utils\Resources\VistaManAsInvoker.manifest</Manifest_File>

View File

@ -92,13 +92,13 @@ type
{ Worker implementations } { Worker implementations }
TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker) TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker)
protected protected
procedure RegisterVariables; override; procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override;
procedure HandleData(AData: Pointer); override; procedure HandleData(AData: Pointer); override;
end; end;
TFSXGearFunctionWorker = class(TCustomFSXFunctionWorker) TFSXGearFunctionWorker = class(TCustomFSXFunctionWorker)
protected protected
procedure RegisterVariables; override; procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override;
procedure HandleData(AData: Pointer); override; procedure HandleData(AData: Pointer); override;
end; end;
@ -106,7 +106,7 @@ type
private private
FStateMask: Integer; FStateMask: Integer;
protected protected
procedure RegisterVariables; override; procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override;
procedure HandleData(AData: Pointer); override; procedure HandleData(AData: Pointer); override;
public public
property StateMask: Integer read FStateMask write FStateMask; property StateMask: Integer read FStateMask write FStateMask;
@ -140,21 +140,21 @@ end;
{ TFSXEngineFunctionWorker } { TFSXEngineFunctionWorker }
procedure TFSXEngineFunctionWorker.RegisterVariables; procedure TFSXEngineFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition);
var var
engineIndex: Integer; engineIndex: Integer;
begin begin
Definition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32);
for engineIndex := 1 to FSX_MAX_ENGINES do for engineIndex := 1 to FSX_MAX_ENGINES do
Definition.AddVariable(Format('GENERAL ENG COMBUSTION:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable(Format('GENERAL ENG COMBUSTION:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
for engineIndex := 1 to FSX_MAX_ENGINES do for engineIndex := 1 to FSX_MAX_ENGINES do
Definition.AddVariable(Format('ENG FAILED:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable(Format('ENG FAILED:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
for engineIndex := 1 to FSX_MAX_ENGINES do for engineIndex := 1 to FSX_MAX_ENGINES do
Definition.AddVariable(Format('ENG ON FIRE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable(Format('ENG ON FIRE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
end; end;
@ -236,12 +236,12 @@ end;
{ TFSXGearFunctionWorker } { TFSXGearFunctionWorker }
procedure TFSXGearFunctionWorker.RegisterVariables; procedure TFSXGearFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition);
begin begin
Definition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
Definition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); ADefinition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64);
Definition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
Definition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
end; end;
@ -349,9 +349,9 @@ end;
{ TFSXLightStatesFunctionWorker } { TFSXLightStatesFunctionWorker }
procedure TFSXLightStatesFunctionWorker.RegisterVariables; procedure TFSXLightStatesFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition);
begin begin
Definition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32); ADefinition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32);
end; end;

View File

@ -18,7 +18,7 @@ type
TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider, IFSXSimConnectObserver) TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider, IFSXSimConnectObserver)
private private
FSimConnect: IFSXSimConnect; FSimConnect: TInterfacedObject;
FSimConnectLock: TCriticalSection; FSimConnectLock: TCriticalSection;
protected protected
procedure RegisterFunctions; override; procedure RegisterFunctions; override;
@ -41,6 +41,8 @@ type
FDisplayName: string; FDisplayName: string;
FUID: string; FUID: string;
protected protected
function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; override;
property Provider: TFSXLEDFunctionProvider read FProvider; property Provider: TFSXLEDFunctionProvider read FProvider;
protected protected
function GetCategoryName: string; override; function GetCategoryName: string; override;
@ -54,26 +56,30 @@ type
TCustomFSXFunctionClass = class of TCustomFSXFunction; TCustomFSXFunctionClass = class of TCustomFSXFunction;
TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker, IFSXSimConnectDataHandler) TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker)
private private
FDataHandler: IFSXSimConnectDataHandler;
FDefinitionID: Cardinal;
FSimConnect: IFSXSimConnect; FSimConnect: IFSXSimConnect;
FDefinition: IFSXSimConnectDefinition;
FCurrentStateLock: TCriticalSection; FCurrentStateLock: TCriticalSection;
FCurrentState: ILEDStateWorker; FCurrentState: ILEDStateWorker;
protected protected
procedure RegisterVariables; virtual; abstract; procedure RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); override;
procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); virtual; abstract;
procedure SetCurrentState(const AUID: string); procedure SetCurrentState(const AUID: string; ANotifyObservers: Boolean = True); overload; virtual;
procedure SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean = True); overload; virtual;
procedure SetSimConnect(const Value: IFSXSimConnect); virtual;
property Definition: IFSXSimConnectDefinition read FDefinition; property DataHandler: IFSXSimConnectDataHandler read FDataHandler;
property SimConnect: IFSXSimConnect read FSimConnect; property DefinitionID: Cardinal read FDefinitionID;
property SimConnect: IFSXSimConnect read FSimConnect write SetSimConnect;
protected protected
function GetCurrentState: ILEDStateWorker; override; function GetCurrentState: ILEDStateWorker; override;
{ IFSXSimConnectDataHandler }
procedure HandleData(AData: Pointer); virtual; abstract; procedure HandleData(AData: Pointer); virtual; abstract;
public public
constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); override;
destructor Destroy; override; destructor Destroy; override;
end; end;
@ -89,6 +95,20 @@ uses
SimConnect; SimConnect;
type
TCustomFSXFunctionWorkerDataHandler = class(TInterfacedObject, IFSXSimConnectDataHandler)
private
FWorker: TCustomFSXFunctionWorker;
protected
{ IFSXSimConnectDataHandler }
procedure HandleData(AData: Pointer);
property Worker: TCustomFSXFunctionWorker read FWorker;
public
constructor Create(AWorker: TCustomFSXFunctionWorker);
end;
{ TFSXLEDFunctionProvider } { TFSXLEDFunctionProvider }
constructor TFSXLEDFunctionProvider.Create; constructor TFSXLEDFunctionProvider.Create;
@ -167,11 +187,13 @@ begin
try try
if not Assigned(FSimConnect) then if not Assigned(FSimConnect) then
begin begin
{ Keep an object reference so we don't increment the reference count.
We'll know when it's gone through the ObserveDestroy. }
FSimConnect := TFSXSimConnectInterface.Create; FSimConnect := TFSXSimConnectInterface.Create;
FSimConnect.Attach(Self); (FSimConnect as IFSXSimConnect).Attach(Self);
end; end;
Result := FSimConnect; Result := (FSimConnect as IFSXSimConnect);
finally finally
FSimConnectLock.Release; FSimConnectLock.Release;
end; end;
@ -189,6 +211,14 @@ begin
end; end;
function TCustomFSXFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker;
begin
Result := inherited DoCreateWorker(ASettings);
(Result as TCustomFSXFunctionWorker).SimConnect := Provider.GetSimConnect;
end;
function TCustomFSXFunction.GetCategoryName: string; function TCustomFSXFunction.GetCategoryName: string;
begin begin
Result := FSXCategory; Result := FSXCategory;
@ -208,18 +238,16 @@ end;
{ TCustomFSXFunctionWorker } { TCustomFSXFunctionWorker }
constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings);
begin begin
inherited Create(AStates, ASettings);
FCurrentStateLock := TCriticalSection.Create; FCurrentStateLock := TCriticalSection.Create;
FSimConnect := ASimConnect;
FDefinition := ASimConnect.CreateDefinition; { We can't pass ourselves as the Data Handler, as it would keep a reference to
RegisterVariables; this worker from the SimConnect interface. That'd mean the worker never
gets destroyed, and SimConnect never shuts down. Hence this proxy class. }
FDataHandler := TCustomFSXFunctionWorkerDataHandler.Create(Self);
// TODO pass self as callback for this definition inherited Create(AStates, ASettings);
ASimConnect.AddDefinition(FDefinition, Self);
end; end;
@ -227,7 +255,20 @@ destructor TCustomFSXFunctionWorker.Destroy;
begin begin
FreeAndNil(FCurrentStateLock); FreeAndNil(FCurrentStateLock);
inherited; if DefinitionID <> 0 then
SimConnect.RemoveDefinition(DefinitionID, DataHandler);
inherited Destroy;
end;
procedure TCustomFSXFunctionWorker.RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings);
begin
inherited RegisterStates(AStates, ASettings);
{ Make sure we have a default state }
if States.Count > 0 then
SetCurrentState((States[0] as ILEDStateWorker), False);
end; end;
@ -242,17 +283,21 @@ begin
end; end;
procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string); procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string; ANotifyObservers: Boolean);
var begin
newState: ILEDStateWorker; SetCurrentState(FindState(AUID), ANotifyObservers);
end;
procedure TCustomFSXFunctionWorker.SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean);
begin begin
FCurrentStateLock.Acquire; FCurrentStateLock.Acquire;
try try
newState := FindState(AUID); if AState <> FCurrentState then
if newState <> FCurrentState then
begin begin
FCurrentState := newState; FCurrentState := AState;
if ANotifyObservers then
NotifyObservers; NotifyObservers;
end; end;
finally finally
@ -261,6 +306,38 @@ begin
end; end;
procedure TCustomFSXFunctionWorker.SetSimConnect(const Value: IFSXSimConnect);
var
definition: IFSXSimConnectDefinition;
begin
FSimConnect := Value;
if Assigned(SimConnect) then
begin
definition := SimConnect.CreateDefinition;
RegisterVariables(definition);
FDefinitionID := SimConnect.AddDefinition(definition, DataHandler);
end;
end;
{ TCustomFSXFunctionWorkerDataHandler }
constructor TCustomFSXFunctionWorkerDataHandler.Create(AWorker: TCustomFSXFunctionWorker);
begin
inherited Create;
FWorker := AWorker;
end;
procedure TCustomFSXFunctionWorkerDataHandler.HandleData(AData: Pointer);
begin
Worker.HandleData(AData);
end;
initialization initialization
TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create); TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create);

View File

@ -2,6 +2,8 @@ unit FSXResources;
interface interface
const const
FSXSimConnectAppName = 'G940 LED Control';
FSXProviderUID = 'fsx'; FSXProviderUID = 'fsx';
FSXCategory = 'Flight Simulator X'; FSXCategory = 'Flight Simulator X';
FSXCategoryLights = FSXCategory + ' - Lights'; FSXCategoryLights = FSXCategory + ' - Lights';

View File

@ -23,8 +23,8 @@ type
procedure Detach(AObserver: IFSXSimConnectObserver); procedure Detach(AObserver: IFSXSimConnectObserver);
function CreateDefinition: IFSXSimConnectDefinition; function CreateDefinition: IFSXSimConnectDefinition;
procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); function AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer;
procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); procedure RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
@ -33,36 +33,149 @@ type
implementation implementation
uses uses
Generics.Collections,
System.Math,
System.SyncObjs,
System.SysUtils, System.SysUtils,
Winapi.Windows,
SimConnect; OtlComm,
OtlCommon,
SimConnect,
FSXResources;
const const
TM_ADDDEFINITION = 3001; TM_ADDDEFINITION = 3001;
TM_REMOVEDEFINITION = 3002; TM_REMOVEDEFINITION = 3002;
TM_TRYSIMCONNECT = 3003;
TIMER_TRYSIMCONNECT = 201;
INTERVAL_TRYSIMCONNECT = 5000;
type type
TFSXSimConnectClient = class(TOmniWorker) TFSXSimConnectDefinitionRef = class(TObject)
private
FDefinition: IFSXSimConnectDefinitionAccess;
FDataHandlers: TInterfaceList;
protected
property DataHandlers: TInterfaceList read FDataHandlers;
public
constructor Create(ADefinition: IFSXSimConnectDefinitionAccess);
destructor Destroy; override;
procedure Attach(ADataHandler: IFSXSimConnectDataHandler);
procedure Detach(ADataHandler: IFSXSimConnectDataHandler);
procedure HandleData(AData: Pointer);
property Definition: IFSXSimConnectDefinitionAccess read FDefinition;
end; end;
TFSXSimConnectDefinition = class(TInterfacedObject, IFSXSimConnectDefinition) TFSXSimConnectDefinitionMap = TDictionary<Cardinal, TFSXSimConnectDefinitionRef>;
TFSXSimConnectClient = class(TOmniWorker)
private
FDefinitions: TFSXSimConnectDefinitionMap;
FLastDefinitionID: Cardinal;
FSimConnectHandle: THandle;
FSimConnectDataEvent: TEvent;
protected
procedure TMAddDefinition(var Msg: TOmniMessage); message TM_ADDDEFINITION;
procedure TMRemoveDefinition(var Msg: TOmniMessage); message TM_REMOVEDEFINITION;
procedure TMTrySimConnect(var Msg: TOmniMessage); message TM_TRYSIMCONNECT;
procedure HandleSimConnectDataEvent;
protected
function Initialize: Boolean; override;
procedure Cleanup; override;
procedure TrySimConnect;
procedure RegisterDefinitions;
procedure RegisterDefinition(ADefinitionID: Cardinal; ADefinition: IFSXSimConnectDefinitionAccess);
function SameDefinition(ADefinition1, ADefinition2: IFSXSimConnectDefinitionAccess): Boolean;
property Definitions: TFSXSimConnectDefinitionMap read FDefinitions;
property LastDefinitionID: Cardinal read FLastDefinitionID;
property SimConnectHandle: THandle read FSimConnectHandle;
property SimConnectDataEvent: TEvent read FSimConnectDataEvent;
end;
TFSXSimConnectVariable = class(TInterfacedPersistent, IFSXSimConnectVariable)
private
FVariableName: string;
FUnitsName: string;
FDataType: SIMCONNECT_DATAType;
FEpsilon: Single;
protected
{ IFSXSimConnectVariable }
function GetVariableName: string;
function GetUnitsName: string;
function GetDataType: SIMCONNECT_DATAType;
function GetEpsilon: Single;
public
constructor Create(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single);
end;
TFSXSimConnectVariableList = TObjectList<TFSXSimConnectVariable>;
TFSXSimConnectDefinition = class(TInterfacedObject, IFSXSimConnectDefinition, IFSXSimConnectDefinitionAccess)
private private
FSimConnect: IFSXSimConnect; FSimConnect: IFSXSimConnect;
FVariables: TFSXSimConnectVariableList;
protected protected
property SimConnect: IFSXSimConnect read FSimConnect; property SimConnect: IFSXSimConnect read FSimConnect;
property Variables: TFSXSimConnectVariableList read FVariables;
protected protected
{ IFSXSimConnectDefinition } { IFSXSimConnectDefinition }
procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0); procedure AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single = 0);
procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer);
{ IFSXSimConnectDefinitionAccess }
function GetVariableCount: Integer;
function GetVariable(AIndex: Integer): IFSXSimConnectVariable;
public public
constructor Create(ASimConnect: IFSXSimConnect); constructor Create;
destructor Destroy; override;
end; end;
TAddDefinitionValue = class(TOmniWaitableValue)
private
FDataHandler: IFSXSimConnectDataHandler;
FDefinition: IFSXSimConnectDefinition;
FDefinitionID: Cardinal;
procedure SetDefinitionID(const Value: Cardinal);
public
constructor Create(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler);
property DataHandler: IFSXSimConnectDataHandler read FDataHandler;
property Definition: IFSXSimConnectDefinition read FDefinition;
property DefinitionID: Cardinal read FDefinitionID write SetDefinitionID;
end;
TRemoveDefinitionValue = class(TOmniWaitableValue)
private
FDataHandler: IFSXSimConnectDataHandler;
FDefinitionID: Cardinal;
public
constructor Create(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
property DataHandler: IFSXSimConnectDataHandler read FDataHandler;
property DefinitionID: Cardinal read FDefinitionID;
end;
{ TFSXSimConnectInterface } { TFSXSimConnectInterface }
constructor TFSXSimConnectInterface.Create; constructor TFSXSimConnectInterface.Create;
@ -70,21 +183,30 @@ var
worker: IOmniWorker; worker: IOmniWorker;
begin begin
worker := TFSXSimConnectClient.Create; inherited Create;
FClient := CreateTask(worker);
FObservers := TInterfaceList.Create; FObservers := TInterfaceList.Create;
worker := TFSXSimConnectClient.Create;
FClient := CreateTask(worker).Run;
end; end;
destructor TFSXSimConnectInterface.Destroy; destructor TFSXSimConnectInterface.Destroy;
var
observer: IInterface;
begin begin
for observer in Observers do
(observer as IFSXSimConnectObserver).ObserveDestroy(Self);
FreeAndNil(FObservers); FreeAndNil(FObservers);
// TODO this doesn't get triggered yet. The connection is killed fine, but not because of us. Needs work.
FClient.Terminate; FClient.Terminate;
FClient := nil; FClient := nil;
inherited; inherited Destroy;
end; end;
@ -102,41 +224,386 @@ end;
function TFSXSimConnectInterface.CreateDefinition: IFSXSimConnectDefinition; function TFSXSimConnectInterface.CreateDefinition: IFSXSimConnectDefinition;
begin begin
Result := TFSXSimConnectDefinition.Create(Self); Result := TFSXSimConnectDefinition.Create;
end; end;
procedure TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); function TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer;
var
addDefinition: TAddDefinitionValue;
begin begin
Client.Comm.Send(TM_ADDDEFINITION, [ADefinition, ADataHandler]); addDefinition := TAddDefinitionValue.Create(ADefinition, ADataHandler);
// TODO pass to thread; if definition already exists (same variables), link to existing definition to avoid too many SimConnect definition Client.Comm.Send(TM_ADDDEFINITION, addDefinition);
addDefinition.WaitFor(INFINITE);
Result := addDefinition.DefinitionID;
end; end;
procedure TFSXSimConnectInterface.RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); procedure TFSXSimConnectInterface.RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
var
removeDefinition: TRemoveDefinitionValue;
begin begin
Client.Comm.Send(TM_REMOVEDEFINITION, ADataHandler); removeDefinition := TRemoveDefinitionValue.Create(ADefinitionID, ADataHandler);
Client.Comm.Send(TM_REMOVEDEFINITION, removeDefinition);
removeDefinition.WaitFor(INFINITE);
end; end;
{ TFSXSimConnectDefinition } { TFSXSimConnectDefinition }
constructor TFSXSimConnectDefinition.Create(ASimConnect: IFSXSimConnect); constructor TFSXSimConnectDefinition.Create;
begin begin
inherited Create;
FVariables := TFSXSimConnectVariableList.Create(True);
end; end;
procedure TFSXSimConnectDefinition.AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single); destructor TFSXSimConnectDefinition.Destroy;
begin begin
FreeAndNil(FVariables);
inherited Destroy;
end; end;
procedure TFSXSimConnectDefinition.Apply(ASimConnectHandle: THandle; ADefinitionID: Integer); procedure TFSXSimConnectDefinition.AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single);
begin begin
// SimConnect_AddToDataDefinition(ASimConnectHandle, ADefinitionID, Variables.Add(TFSXSimConnectVariable.Create(AVariableName, AUnitsName, ADataType, AEpsilon));
// AnsiString(AVariableName), AnsiString(AUnitsName), ADatumType, AEpsilon, 0); end;
function TFSXSimConnectDefinition.GetVariable(AIndex: Integer): IFSXSimConnectVariable;
begin
Result := Variables[AIndex];
end;
function TFSXSimConnectDefinition.GetVariableCount: Integer;
begin
Result := Variables.Count;
end;
{ TFSXSimConnectClient }
function TFSXSimConnectClient.Initialize: Boolean;
begin
Result := inherited Initialize;
if not Result then
exit;
FDefinitions := TFSXSimConnectDefinitionMap.Create;
FSimConnectDataEvent := TEvent.Create(nil, False, False, '');
Task.RegisterWaitObject(SimConnectDataEvent.Handle, HandleSimConnectDataEvent);
TrySimConnect;
end;
procedure TFSXSimConnectClient.Cleanup;
begin
// TODO unregister definitions ?
if SimConnectHandle <> 0 then
SimConnect_Close(SimConnectHandle);
FreeAndNil(FSimConnectDataEvent);
FreeAndNil(FDefinitions);
inherited Cleanup;
end;
procedure TFSXSimConnectClient.TrySimConnect;
begin
if SimConnectHandle <> 0 then
exit;
if InitSimConnect then
begin
if SimConnect_Open(FSimConnectHandle, FSXSimConnectAppName, 0, 0, SimConnectDataEvent.Handle, 0) = S_OK then
begin
Task.ClearTimer(TIMER_TRYSIMCONNECT);
RegisterDefinitions;
end;
end;
if SimConnectHandle = 0 then
Task.SetTimer(TIMER_TRYSIMCONNECT, INTERVAL_TRYSIMCONNECT, TM_TRYSIMCONNECT);
end;
procedure TFSXSimConnectClient.HandleSimConnectDataEvent;
var
data: PSimConnectRecv;
dataSize: Cardinal;
simObjectData: PSimConnectRecvSimObjectData;
definitionRef: TFSXSimConnectDefinitionRef;
begin
while (SimConnectHandle <> 0) and
(SimConnect_GetNextDispatch(SimConnectHandle, data, dataSize) = S_OK) do
begin
case SIMCONNECT_RECV_ID(data^.dwID) of
SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
begin
simObjectData := PSimConnectRecvSimObjectData(data);
if Definitions.ContainsKey(simObjectData^.dwDefineID) then
begin
definitionRef := Definitions[simObjectData^.dwDefineID];
definitionRef.HandleData(@simObjectData^.dwData);
end;
end;
SIMCONNECT_RECV_ID_QUIT:
begin
FSimConnectHandle := 0;
Task.SetTimer(TIMER_TRYSIMCONNECT, INTERVAL_TRYSIMCONNECT, TM_TRYSIMCONNECT);
end;
end;
end;
end;
procedure TFSXSimConnectClient.RegisterDefinitions;
var
definitionID: Cardinal;
begin
if SimConnectHandle = 0 then
exit;
for definitionID in Definitions.Keys do
RegisterDefinition(definitionID, Definitions[definitionID].Definition);
end;
procedure TFSXSimConnectClient.RegisterDefinition(ADefinitionID: Cardinal; ADefinition: IFSXSimConnectDefinitionAccess);
var
variableIndex: Integer;
variable: IFSXSimConnectVariable;
begin
if SimConnectHandle = 0 then
exit;
for variableIndex := 0 to Pred(ADefinition.GetVariableCount) do
begin
variable := ADefinition.GetVariable(variableIndex);
SimConnect_AddToDataDefinition(SimConnectHandle, ADefinitionID,
AnsiString(variable.GetVariableName),
AnsiString(variable.GetUnitsName),
variable.GetDataType,
variable.GetEpsilon);
end;
SimConnect_RequestDataOnSimObject(SimConnectHandle, ADefinitionID, ADefinitionID,
SIMCONNECT_OBJECT_ID_USER,
SIMCONNECT_PERIOD_SIM_FRAME,
SIMCONNECT_DATA_REQUEST_FLAG_CHANGED);
end;
function TFSXSimConnectClient.SameDefinition(ADefinition1, ADefinition2: IFSXSimConnectDefinitionAccess): Boolean;
var
variableIndex: Integer;
variable1: IFSXSimConnectVariable;
variable2: IFSXSimConnectVariable;
begin
if ADefinition1.GetVariableCount = ADefinition2.GetVariableCount then
begin
Result := True;
{ Order is very important in the definitions, as the Data Handler depends
on it to interpret the data. }
for variableIndex := 0 to Pred(ADefinition1.GetVariableCount) do
begin
variable1 := ADefinition1.GetVariable(variableIndex);
variable2 := ADefinition2.GetVariable(variableIndex);
if (variable1.GetVariableName <> variable2.GetVariableName) or
(variable1.GetUnitsName <> variable2.GetUnitsName) or
(variable1.GetDataType <> variable2.GetDataType) or
(not SameValue(variable1.GetEpsilon, variable2.GetEpsilon, 0.00001)) then
begin
Result := False;
break;
end;
end;
end else
Result := False;
end;
procedure TFSXSimConnectClient.TMAddDefinition(var Msg: TOmniMessage);
var
addDefinition: TAddDefinitionValue;
definitionID: Cardinal;
definitionRef: TFSXSimConnectDefinitionRef;
definitionAccess: IFSXSimConnectDefinitionAccess;
hasDefinition: Boolean;
begin
addDefinition := Msg.MsgData;
definitionAccess := (addDefinition.Definition as IFSXSimConnectDefinitionAccess);
hasDefinition := False;
{ Attempt to re-use existing definition to save on SimConnect traffic }
for definitionID in Definitions.Keys do
begin
definitionRef := Definitions[definitionID];
if SameDefinition(definitionRef.Definition, definitionAccess) then
begin
definitionRef.Attach(addDefinition.DataHandler);
addDefinition.DefinitionID := definitionID;
hasDefinition := True;
break;
end;
end;
if not hasDefinition then
begin
{ Add as new definition }
Inc(FLastDefinitionID);
definitionRef := TFSXSimConnectDefinitionRef.Create(definitionAccess);
definitionRef.Attach(addDefinition.DataHandler);
Definitions.Add(LastDefinitionID, definitionRef);
addDefinition.DefinitionID := LastDefinitionID;
{ Register with SimConnect }
RegisterDefinition(LastDefinitionID, definitionAccess);
end;
end;
procedure TFSXSimConnectClient.TMRemoveDefinition(var Msg: TOmniMessage);
var
removeDefinition: TRemoveDefinitionValue;
begin
removeDefinition := Msg.MsgData;
// TODO actually remove the definition
removeDefinition.Signal;
end;
procedure TFSXSimConnectClient.TMTrySimConnect(var Msg: TOmniMessage);
begin
TrySimConnect;
end;
{ TFSXSimConnectDefinitionRef }
constructor TFSXSimConnectDefinitionRef.Create(ADefinition: IFSXSimConnectDefinitionAccess);
begin
inherited Create;
FDataHandlers := TInterfaceList.Create;
FDefinition := ADefinition;
end;
destructor TFSXSimConnectDefinitionRef.Destroy;
begin
FreeAndNil(FDataHandlers);
inherited Destroy;
end;
procedure TFSXSimConnectDefinitionRef.HandleData(AData: Pointer);
var
dataHandler: IInterface;
begin
for dataHandler in DataHandlers do
(dataHandler as IFSXSimConnectDataHandler).HandleData(AData);
end;
procedure TFSXSimConnectDefinitionRef.Attach(ADataHandler: IFSXSimConnectDataHandler);
begin
DataHandlers.Add(ADataHandler as IFSXSimConnectDataHandler);
end;
procedure TFSXSimConnectDefinitionRef.Detach(ADataHandler: IFSXSimConnectDataHandler);
begin
DataHandlers.Remove(ADataHandler as IFSXSimConnectDataHandler);
end;
{ TFSXSimConnectVariable }
constructor TFSXSimConnectVariable.Create(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single);
begin
inherited Create;
FVariableName := AVariableName;
FUnitsName := AUnitsName;
FDataType := ADataType;
FEpsilon := AEpsilon;
end;
function TFSXSimConnectVariable.GetVariableName: string;
begin
Result := FVariableName;
end;
function TFSXSimConnectVariable.GetUnitsName: string;
begin
Result := FUnitsName;
end;
function TFSXSimConnectVariable.GetDataType: SIMCONNECT_DATAType;
begin
Result := FDataType;
end;
function TFSXSimConnectVariable.GetEpsilon: Single;
begin
Result := FEpsilon;
end;
{ TAddDefinitionValue }
constructor TAddDefinitionValue.Create(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler);
begin
inherited Create;
FDefinition := ADefinition;
FDataHandler := ADataHandler;
end;
procedure TAddDefinitionValue.SetDefinitionID(const Value: Cardinal);
begin
FDefinitionID := Value;
Signal;
end;
{ TRemoveDefinitionValue }
constructor TRemoveDefinitionValue.Create(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
begin
inherited Create;
FDefinitionID := ADefinitionID;
FDataHandler := ADataHandler;
end; end;
end. end.

View File

@ -28,18 +28,32 @@ type
procedure Detach(AObserver: IFSXSimConnectObserver); procedure Detach(AObserver: IFSXSimConnectObserver);
function CreateDefinition: IFSXSimConnectDefinition; function CreateDefinition: IFSXSimConnectDefinition;
procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); function AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer;
procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); procedure RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
end; end;
IFSXSimConnectDefinition = interface IFSXSimConnectDefinition = interface
['{F1EAB3B1-0A3D-4B06-A75F-823E15C313B8}'] ['{F1EAB3B1-0A3D-4B06-A75F-823E15C313B8}']
procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0); procedure AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single = 0);
procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer);
end; end;
IFSXSimConnectVariable = interface
['{A41AD003-77C0-4E34-91E3-B0BAADD08FCE}']
function GetVariableName: string;
function GetUnitsName: string;
function GetDataType: SIMCONNECT_DATAType;
function GetEpsilon: Single;
end;
IFSXSimConnectDefinitionAccess = interface
['{2592534C-0344-4442-8A5F-1AB34B96E1B5}']
function GetVariableCount: Integer;
function GetVariable(AIndex: Integer): IFSXSimConnectVariable;
end;
const const
FSX_UNIT_PERCENT = 'percent'; FSX_UNIT_PERCENT = 'percent';

View File

@ -13,7 +13,9 @@ uses
const const
TM_FINDTHROTTLEDEVICE = 2001; TM_FINDTHROTTLEDEVICE = 2001;
TM_NOTIFY_DEVICESTATE = 2002; TM_TESTTHROTTLEDEVICE = 2002;
TM_NOTIFY_DEVICESTATE = 2003;
type type
@ -21,10 +23,13 @@ type
private private
FDirectInput: IDirectInput8; FDirectInput: IDirectInput8;
FThrottleDevice: IDirectInputDevice8; FThrottleDevice: IDirectInputDevice8;
FTHrottleDeviceGUID: TGUID;
protected protected
procedure MsgFindThrottleDevice(var msg: TOmniMessage); message TM_FINDTHROTTLEDEVICE; procedure TMFindThrottleDevice(var Msg: TOmniMessage); message TM_FINDTHROTTLEDEVICE;
procedure TMTestThrottleDevice(var Msg: TOmniMessage); message TM_TESTTHROTTLEDEVICE;
protected protected
function Initialize: Boolean; override; function Initialize: Boolean; override;
procedure Cleanup; override;
procedure FindThrottleDevice; procedure FindThrottleDevice;
procedure FoundThrottleDevice(ADeviceGUID: TGUID); procedure FoundThrottleDevice(ADeviceGUID: TGUID);
@ -35,6 +40,7 @@ type
property DirectInput: IDirectInput8 read FDirectInput; property DirectInput: IDirectInput8 read FDirectInput;
property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice; property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice;
property ThrottleDeviceGUID: TGUID read FTHrottleDeviceGUID;
end; end;
@ -63,23 +69,6 @@ const
G940_BUTTONCOUNT = 8; G940_BUTTONCOUNT = 8;
(*
type
TRunInMainThreadSetLEDs = class(TOmniWaitableValue, IRunInMainThread)
private
FDevice: IDirectInputDevice8;
FRed: Byte;
FGreen: Byte;
protected
{ IRunInMainThread }
procedure Execute;
public
constructor Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte);
end;
*)
function EnumDevicesProc(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; stdcall; function EnumDevicesProc(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; stdcall;
var var
vendorID: Word; vendorID: Word;
@ -127,81 +116,13 @@ begin
end; end;
{ procedure TG940LEDStateConsumer.Cleanup;
procedure TG940LEDStateConsumer.ResetLEDState;
begin begin
FRed := 0; inherited Cleanup;
FGreen := $FF;
inherited;
end;
procedure TG940LEDStateConsumer.LEDStateChanged(ALEDIndex: Integer; AState: TLEDState);
procedure SetBit(var AMask: Byte; ABit: Integer; ASet: Boolean); inline;
begin
if ASet then
AMask := AMask or (1 shl ABit)
else
AMask := AMask and not (1 shl ABit);
end;
var
red: Boolean;
green: Boolean;
begin
red := False;
green := False;
case AState of
lsGreen:
green := True;
lsAmber:
begin
red := True;
green := True;
end;
lsRed:
red := True;
lsWarning:
begin
red := True;
green := True;
StartBlinkTimer;
end;
lsError:
begin
red := True;
StartBlinkTimer;
end;
end;
SetBit(FRed, ALEDIndex, red);
SetBit(FGreen, ALEDIndex, green);
inherited;
end;
}
{
procedure TG940LEDStateConsumer.Changed;
begin
inherited;
if Assigned(ThrottleDevice) then if Assigned(ThrottleDevice) then
{ Logitech SDK will not change the color outside of the main thread SetLEDs(ThrottleDevice, 0, $FF);
RunInMainThread(TRunInMainThreadSetLEDs.Create(ThrottleDevice, FRed, FGreen), Destroying);
end; end;
}
procedure TG940LEDStateConsumer.FindThrottleDevice; procedure TG940LEDStateConsumer.FindThrottleDevice;
@ -222,8 +143,11 @@ end;
procedure TG940LEDStateConsumer.FoundThrottleDevice(ADeviceGUID: TGUID); procedure TG940LEDStateConsumer.FoundThrottleDevice(ADeviceGUID: TGUID);
begin begin
if DirectInput.CreateDevice(ADeviceGUID, FThrottleDevice, nil) = S_OK then if DirectInput.CreateDevice(ADeviceGUID, FThrottleDevice, nil) = S_OK then
begin
FTHrottleDeviceGUID := ADeviceGUID;
SetDeviceState(DEVICESTATE_FOUND); SetDeviceState(DEVICESTATE_FOUND);
end; end;
end;
procedure TG940LEDStateConsumer.SetDeviceState(AState: Integer); procedure TG940LEDStateConsumer.SetDeviceState(AState: Integer);
@ -279,28 +203,22 @@ begin
end; end;
procedure TG940LEDStateConsumer.MsgFindThrottleDevice(var msg: TOmniMessage); procedure TG940LEDStateConsumer.TMFindThrottleDevice(var Msg: TOmniMessage);
begin begin
FindThrottleDevice; FindThrottleDevice;
end; end;
{ TRunInMainThreadSetLEDs } procedure TG940LEDStateConsumer.TMTestThrottleDevice(var Msg: TOmniMessage);
(*
constructor TRunInMainThreadSetLEDs.Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte);
begin begin
inherited Create; if Assigned(ThrottleDevice) then
FDevice := ADevice;
FRed := ARed;
FGreen := AGreen;
end;
procedure TRunInMainThreadSetLEDs.Execute;
begin begin
SetLEDs(FDevice, FRed, FGreen); if DirectInput.GetDeviceStatus(ThrottleDeviceGUID) = DI_NOTATTACHED then
begin
FThrottleDevice := nil;
SetDeviceState(DEVICESTATE_NOTFOUND);
end;
end;
end; end;
*)
end. end.

View File

@ -81,8 +81,8 @@ type
function GetCurrentState: ILEDStateWorker; virtual; abstract; function GetCurrentState: ILEDStateWorker; virtual; abstract;
public public
constructor Create; overload; constructor Create; overload; virtual;
constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload; constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload; virtual;
destructor Destroy; override; destructor Destroy; override;
end; end;
@ -138,7 +138,7 @@ destructor TCustomMultiStateLEDFunction.Destroy;
begin begin
FreeAndNil(FStates); FreeAndNil(FStates);
inherited; inherited Destroy;
end; end;
@ -229,6 +229,8 @@ var
begin begin
Result := nil; Result := nil;
if not Assigned(States) then
exit;
for state in States do for state in States do
if (state as ICustomLEDState).GetUID = AUID then if (state as ICustomLEDState).GetUID = AUID then
@ -263,7 +265,7 @@ destructor TCustomLEDFunctionProvider.Destroy;
begin begin
FreeAndNil(FFunctions); FreeAndNil(FFunctions);
inherited; inherited Destroy;
end; end;

View File

@ -114,7 +114,7 @@ destructor TLEDFunctionRegistry.Destroy;
begin begin
FreeAndNil(FProviders); FreeAndNil(FProviders);
inherited; inherited Destroy;
end; end;
@ -155,7 +155,7 @@ destructor TLEDFunctionProviderList.Destroy;
begin begin
FreeAndNil(FList); FreeAndNil(FList);
inherited; inherited Destroy;
end; end;

View File

@ -21,13 +21,7 @@ const
type type
IRunInMainThread = interface(IOmniWaitableValue) TLEDStateConsumer = class(TOmniWorker, ILEDFunctionObserver)
['{68B8F2F7-ED40-4078-9D99-503D7AFA068B}']
procedure Execute;
end;
TLEDStateConsumer = class(TOmniWorker)
private private
FButtonWorkers: TInterfaceList; FButtonWorkers: TInterfaceList;
FButtonColors: TInterfaceList; FButtonColors: TInterfaceList;
@ -42,6 +36,9 @@ type
property ButtonColors: TInterfaceList read FButtonColors; property ButtonColors: TInterfaceList read FButtonColors;
property HasTickTimer: Boolean read FHasTickTimer; property HasTickTimer: Boolean read FHasTickTimer;
protected protected
{ ILEDFunctionObserver }
procedure ObserveUpdate(Sender: ILEDFunctionWorker);
procedure Changed; virtual; procedure Changed; virtual;
procedure Update; virtual; abstract; procedure Update; virtual; abstract;
protected protected
@ -171,22 +168,50 @@ begin
end; end;
procedure TLEDStateConsumer.ObserveUpdate(Sender: ILEDFunctionWorker);
begin
Changed;
end;
procedure TLEDStateConsumer.TMLoadProfile(var Msg: TOmniMessage); procedure TLEDStateConsumer.TMLoadProfile(var Msg: TOmniMessage);
var var
oldWorkers: TInterfaceList;
oldWorker: IInterface;
profile: TProfile; profile: TProfile;
buttonIndex: Integer; buttonIndex: Integer;
worker: ILEDFunctionWorker;
begin begin
profile := Msg.MsgData; profile := Msg.MsgData;
{ Keep a copy of the old workers until all the new ones are initialized,
so we don't get unneccessary SimConnect reconnects. }
oldWorkers := TInterfaceList.Create;
try
for oldWorker in ButtonWorkers do
begin
if Assigned(oldWorker) then
oldWorkers.Add(oldWorker);
end;
ButtonWorkers.Clear; ButtonWorkers.Clear;
for buttonIndex := 0 to Pred(profile.ButtonCount) do for buttonIndex := 0 to Pred(profile.ButtonCount) do
begin begin
if profile.HasButton(buttonIndex) then if profile.HasButton(buttonIndex) then
ButtonWorkers.Add(CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker) begin
else worker := CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker;
ButtonWorkers.Add(worker);
if Assigned(worker) then
worker.Attach(Self);
end else
ButtonWorkers.Add(nil); ButtonWorkers.Add(nil);
end; end;
finally
FreeAndNil(oldWorkers);
end;
Changed; Changed;
end; end;

View File

@ -188,7 +188,7 @@ destructor TProfile.Destroy;
begin begin
FreeAndNil(FButtons); FreeAndNil(FButtons);
inherited; inherited Destroy;
end; end;