diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm
index 5b4ac44..a58a285 100644
--- a/G940LEDControl/Forms/ButtonFunctionFrm.dfm
+++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm
@@ -101,7 +101,6 @@ object ButtonFunctionForm: TButtonFunctionForm
OnFocusChanged = vstFunctionsFocusChanged
OnGetText = vstFunctionsGetText
OnPaintText = vstFunctionsPaintText
- ExplicitHeight = 383
Columns = <
item
Position = 0
@@ -122,7 +121,6 @@ object ButtonFunctionForm: TButtonFunctionForm
Align = alClient
BevelOuter = bvNone
TabOrder = 1
- ExplicitHeight = 383
object pnlName: TPanel
Left = 0
Top = 0
@@ -194,7 +192,6 @@ object ButtonFunctionForm: TButtonFunctionForm
Align = alClient
BorderStyle = bsNone
TabOrder = 1
- ExplicitHeight = 286
end
end
object pnlHeader: TPanel
diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas
index 60072dc..f442d4c 100644
--- a/G940LEDControl/Forms/ButtonFunctionFrm.pas
+++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas
@@ -469,7 +469,7 @@ begin
FreeAndNil(FComboBox);
FreeAndNil(FStateLabel);
- inherited;
+ inherited Destroy;
end;
end.
diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm
index f1ed398..857ac1a 100644
--- a/G940LEDControl/Forms/MainFrm.dfm
+++ b/G940LEDControl/Forms/MainFrm.dfm
@@ -356,6 +356,10 @@ object MainForm: TMainForm
object tsAbout: TTabSheet
Caption = 'About'
ImageIndex = 1
+ ExplicitLeft = 0
+ ExplicitTop = 0
+ ExplicitWidth = 0
+ ExplicitHeight = 0
object lblVersionCaption: TLabel
Left = 16
Top = 67
@@ -842,16 +846,5 @@ object MainForm: TMainForm
ParentFont = False
ExplicitWidth = 401
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
diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas
index bd5a9de..7cb4299 100644
--- a/G940LEDControl/Forms/MainFrm.pas
+++ b/G940LEDControl/Forms/MainFrm.pas
@@ -2,15 +2,15 @@ unit MainFrm;
interface
uses
- Classes,
- Contnrs,
- Controls,
- ComCtrls,
- ExtCtrls,
- Forms,
- Messages,
- StdCtrls,
- Windows,
+ System.Classes,
+ System.Contnrs,
+ Vcl.ComCtrls,
+ Vcl.Controls,
+ Vcl.ExtCtrls,
+ Vcl.Forms,
+ Vcl.StdCtrls,
+ Winapi.Messages,
+ Winapi.Windows,
OtlComm,
OtlEventMonitor,
@@ -20,7 +20,7 @@ uses
X2UtPersistIntf,
LEDStateConsumer,
- Profile;
+ Profile, Vcl.AppEvnts;
const
@@ -31,6 +31,12 @@ const
LED_COUNT = 8;
+ DBT_DEVICEARRIVAL = $8000;
+ DBT_DEVICEREMOVECOMPLETE = $8004;
+ DBT_DEVTYP_DEVICEINTERFACE = $0005;
+ DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = $0004;
+
+
type
TLEDControls = record
ConfigureButton: TButton;
@@ -44,7 +50,6 @@ type
lblG940Throttle: TLabel;
imgStateFound: TImage;
lblG940ThrottleState: TLabel;
- btnRetry: TButton;
PageControl: TPageControl;
pnlG940: TPanel;
tsAbout: TTabSheet;
@@ -91,7 +96,6 @@ type
bvlProfiles: TBevel;
procedure FormCreate(Sender: TObject);
- procedure btnRetryClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
procedure btnCheckUpdatesClick(Sender: TObject);
@@ -107,6 +111,14 @@ type
FActiveProfile: TProfile;
FLoadingProfiles: Boolean;
FStateConsumerTask: IOmniTaskControl;
+
+ FDeviceNotification: Pointer;
+ FG940Found: Boolean;
+ protected
+ procedure RegisterDeviceArrival;
+ procedure UnregisterDeviceArrival;
+
+ procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
protected
procedure FindLEDControls;
procedure LoadProfiles;
@@ -126,9 +138,6 @@ type
procedure EventMonitorTerminated(const task: IOmniTaskControl);
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;
@@ -204,9 +213,10 @@ begin
FProfiles := TProfileList.Create(True);
LoadProfiles;
+ // TODO implement profile changing properly
FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile);
-// LoadFunctions(TFSXLEDStateProvider, FFSXComboBoxes);
-// LoadDefaultProfile;
+
+ RegisterDeviceArrival;
end;
@@ -225,10 +235,63 @@ end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
+ UnregisterDeviceArrival;
+
FreeAndNil(FProfiles);
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;
function ComponentByName(const AName: string; ATag: NativeInt): TComponent;
@@ -417,36 +480,11 @@ begin
imgStateFound.Visible := AFound;
imgStateNotFound.Visible := not AFound;
+
+ FG940Found := AFound;
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);
//var
// checkUpdates: Boolean;
@@ -484,29 +522,6 @@ 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);
var
buttonIndex: NativeInt;
@@ -628,32 +643,31 @@ end;
procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
case msg.MsgID of
- TM_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg);
-// MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg);
-// MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg);
+ TM_NOTIFY_DEVICESTATE:
+ HandleDeviceStateMessage(task, msg);
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?',
'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then
ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL);
-// MSG_NOUPDATE:
-// if msg.MsgData.AsBoolean then
-// MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION)
-// else
-// MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING);
+ MSG_NOUPDATE:
+ if msg.MsgData.AsBoolean then
+ MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION)
+ else
+ MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING);
end;
end;
procedure TMainForm.EventMonitorTerminated(const task: IOmniTaskControl);
begin
-// if task = StateConsumerTask then
-// begin
-// FStateConsumerTask := nil;
-// Close;
-// end else if task.Name = 'CheckForUpdatesThread' then
-// btnCheckUpdates.Enabled := True;
+ if task = StateConsumerTask then
+ begin
+ FStateConsumerTask := nil;
+ Close;
+ end else if task.Name = 'CheckForUpdatesThread' then
+ btnCheckUpdates.Enabled := True;
end;
@@ -667,58 +681,17 @@ begin
SetDeviceState(TEXT_STATE_FOUND, True);
DEVICESTATE_NOTFOUND:
- begin
- SetDeviceState(TEXT_STATE_NOTFOUND, False);
- btnRetry.Visible := True;
- end;
+ SetDeviceState(TEXT_STATE_NOTFOUND, False);
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);
begin
CheckForUpdates(True);
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);
begin
ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL);
diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj
index a3cb490..d357a23 100644
--- a/G940LEDControl/G940LEDControl.dproj
+++ b/G940LEDControl/G940LEDControl.dproj
@@ -98,7 +98,6 @@
False
- true
false
CompanyName=;FileDescription=;FileVersion=0.2.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=0.2;Comments=
F:\Components\X2Utils\Resources\VistaManAsInvoker.manifest
diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas
index 0f8c13c..f829279 100644
--- a/G940LEDControl/Units/FSXLEDFunction.pas
+++ b/G940LEDControl/Units/FSXLEDFunction.pas
@@ -92,13 +92,13 @@ type
{ Worker implementations }
TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker)
protected
- procedure RegisterVariables; override;
+ procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override;
procedure HandleData(AData: Pointer); override;
end;
TFSXGearFunctionWorker = class(TCustomFSXFunctionWorker)
protected
- procedure RegisterVariables; override;
+ procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override;
procedure HandleData(AData: Pointer); override;
end;
@@ -106,7 +106,7 @@ type
private
FStateMask: Integer;
protected
- procedure RegisterVariables; override;
+ procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override;
procedure HandleData(AData: Pointer); override;
public
property StateMask: Integer read FStateMask write FStateMask;
@@ -140,21 +140,21 @@ end;
{ TFSXEngineFunctionWorker }
-procedure TFSXEngineFunctionWorker.RegisterVariables;
+procedure TFSXEngineFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition);
var
engineIndex: Integer;
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
- 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
- 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
- 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;
@@ -236,12 +236,12 @@ end;
{ TFSXGearFunctionWorker }
-procedure TFSXGearFunctionWorker.RegisterVariables;
+procedure TFSXGearFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition);
begin
- Definition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
- Definition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64);
- Definition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
- Definition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
+ ADefinition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
+ ADefinition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64);
+ ADefinition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
+ ADefinition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
end;
@@ -349,9 +349,9 @@ end;
{ TFSXLightStatesFunctionWorker }
-procedure TFSXLightStatesFunctionWorker.RegisterVariables;
+procedure TFSXLightStatesFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition);
begin
- Definition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32);
+ ADefinition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32);
end;
diff --git a/G940LEDControl/Units/FSXLEDFunctionProvider.pas b/G940LEDControl/Units/FSXLEDFunctionProvider.pas
index 19659e1..2a7d61b 100644
--- a/G940LEDControl/Units/FSXLEDFunctionProvider.pas
+++ b/G940LEDControl/Units/FSXLEDFunctionProvider.pas
@@ -18,7 +18,7 @@ type
TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider, IFSXSimConnectObserver)
private
- FSimConnect: IFSXSimConnect;
+ FSimConnect: TInterfacedObject;
FSimConnectLock: TCriticalSection;
protected
procedure RegisterFunctions; override;
@@ -41,6 +41,8 @@ type
FDisplayName: string;
FUID: string;
protected
+ function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; override;
+
property Provider: TFSXLEDFunctionProvider read FProvider;
protected
function GetCategoryName: string; override;
@@ -54,26 +56,30 @@ type
TCustomFSXFunctionClass = class of TCustomFSXFunction;
- TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker, IFSXSimConnectDataHandler)
+ TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker)
private
+ FDataHandler: IFSXSimConnectDataHandler;
+ FDefinitionID: Cardinal;
FSimConnect: IFSXSimConnect;
- FDefinition: IFSXSimConnectDefinition;
FCurrentStateLock: TCriticalSection;
FCurrentState: ILEDStateWorker;
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 SimConnect: IFSXSimConnect read FSimConnect;
+ property DataHandler: IFSXSimConnectDataHandler read FDataHandler;
+ property DefinitionID: Cardinal read FDefinitionID;
+ property SimConnect: IFSXSimConnect read FSimConnect write SetSimConnect;
protected
function GetCurrentState: ILEDStateWorker; override;
- { IFSXSimConnectDataHandler }
procedure HandleData(AData: Pointer); virtual; abstract;
public
- constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect);
+ constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); override;
destructor Destroy; override;
end;
@@ -89,6 +95,20 @@ uses
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 }
constructor TFSXLEDFunctionProvider.Create;
@@ -167,11 +187,13 @@ begin
try
if not Assigned(FSimConnect) then
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.Attach(Self);
+ (FSimConnect as IFSXSimConnect).Attach(Self);
end;
- Result := FSimConnect;
+ Result := (FSimConnect as IFSXSimConnect);
finally
FSimConnectLock.Release;
end;
@@ -189,6 +211,14 @@ begin
end;
+function TCustomFSXFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker;
+begin
+ Result := inherited DoCreateWorker(ASettings);
+
+ (Result as TCustomFSXFunctionWorker).SimConnect := Provider.GetSimConnect;
+end;
+
+
function TCustomFSXFunction.GetCategoryName: string;
begin
Result := FSXCategory;
@@ -208,18 +238,16 @@ end;
{ TCustomFSXFunctionWorker }
-constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect);
+constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings);
begin
- inherited Create(AStates, ASettings);
-
FCurrentStateLock := TCriticalSection.Create;
- FSimConnect := ASimConnect;
- FDefinition := ASimConnect.CreateDefinition;
- RegisterVariables;
+ { We can't pass ourselves as the Data Handler, as it would keep a reference to
+ 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
- ASimConnect.AddDefinition(FDefinition, Self);
+ inherited Create(AStates, ASettings);
end;
@@ -227,7 +255,20 @@ destructor TCustomFSXFunctionWorker.Destroy;
begin
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;
@@ -242,18 +283,22 @@ begin
end;
-procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string);
-var
- newState: ILEDStateWorker;
+procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string; ANotifyObservers: Boolean);
+begin
+ SetCurrentState(FindState(AUID), ANotifyObservers);
+end;
+
+procedure TCustomFSXFunctionWorker.SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean);
begin
FCurrentStateLock.Acquire;
try
- newState := FindState(AUID);
- if newState <> FCurrentState then
+ if AState <> FCurrentState then
begin
- FCurrentState := newState;
- NotifyObservers;
+ FCurrentState := AState;
+
+ if ANotifyObservers then
+ NotifyObservers;
end;
finally
FCurrentStateLock.Release;
@@ -261,6 +306,38 @@ begin
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
TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create);
diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas
index d1dad2a..b6f6300 100644
--- a/G940LEDControl/Units/FSXResources.pas
+++ b/G940LEDControl/Units/FSXResources.pas
@@ -2,6 +2,8 @@ unit FSXResources;
interface
const
+ FSXSimConnectAppName = 'G940 LED Control';
+
FSXProviderUID = 'fsx';
FSXCategory = 'Flight Simulator X';
FSXCategoryLights = FSXCategory + ' - Lights';
diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas
index e55d7e5..3155883 100644
--- a/G940LEDControl/Units/FSXSimConnectClient.pas
+++ b/G940LEDControl/Units/FSXSimConnectClient.pas
@@ -23,8 +23,8 @@ type
procedure Detach(AObserver: IFSXSimConnectObserver);
function CreateDefinition: IFSXSimConnectDefinition;
- procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler);
- procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler);
+ function AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer;
+ procedure RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
public
constructor Create;
destructor Destroy; override;
@@ -33,36 +33,149 @@ type
implementation
uses
+ Generics.Collections,
+ System.Math,
+ System.SyncObjs,
System.SysUtils,
+ Winapi.Windows,
- SimConnect;
+ OtlComm,
+ OtlCommon,
+ SimConnect,
+
+ FSXResources;
const
TM_ADDDEFINITION = 3001;
TM_REMOVEDEFINITION = 3002;
+ TM_TRYSIMCONNECT = 3003;
+
+ TIMER_TRYSIMCONNECT = 201;
+
+ INTERVAL_TRYSIMCONNECT = 5000;
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;
- TFSXSimConnectDefinition = class(TInterfacedObject, IFSXSimConnectDefinition)
+ TFSXSimConnectDefinitionMap = TDictionary;
+
+ 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;
+
+ TFSXSimConnectDefinition = class(TInterfacedObject, IFSXSimConnectDefinition, IFSXSimConnectDefinitionAccess)
private
FSimConnect: IFSXSimConnect;
+ FVariables: TFSXSimConnectVariableList;
protected
property SimConnect: IFSXSimConnect read FSimConnect;
+ property Variables: TFSXSimConnectVariableList read FVariables;
protected
{ IFSXSimConnectDefinition }
- procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0);
- procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer);
+ procedure AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single = 0);
+
+ { IFSXSimConnectDefinitionAccess }
+ function GetVariableCount: Integer;
+ function GetVariable(AIndex: Integer): IFSXSimConnectVariable;
public
- constructor Create(ASimConnect: IFSXSimConnect);
+ constructor Create;
+ destructor Destroy; override;
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 }
constructor TFSXSimConnectInterface.Create;
@@ -70,21 +183,30 @@ var
worker: IOmniWorker;
begin
- worker := TFSXSimConnectClient.Create;
- FClient := CreateTask(worker);
+ inherited Create;
FObservers := TInterfaceList.Create;
+
+ worker := TFSXSimConnectClient.Create;
+ FClient := CreateTask(worker).Run;
end;
destructor TFSXSimConnectInterface.Destroy;
+var
+ observer: IInterface;
+
begin
+ for observer in Observers do
+ (observer as IFSXSimConnectObserver).ObserveDestroy(Self);
+
FreeAndNil(FObservers);
+ // TODO this doesn't get triggered yet. The connection is killed fine, but not because of us. Needs work.
FClient.Terminate;
FClient := nil;
- inherited;
+ inherited Destroy;
end;
@@ -102,41 +224,386 @@ end;
function TFSXSimConnectInterface.CreateDefinition: IFSXSimConnectDefinition;
begin
- Result := TFSXSimConnectDefinition.Create(Self);
+ Result := TFSXSimConnectDefinition.Create;
end;
-procedure TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler);
+function TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer;
+var
+ addDefinition: TAddDefinitionValue;
+
begin
- Client.Comm.Send(TM_ADDDEFINITION, [ADefinition, ADataHandler]);
- // TODO pass to thread; if definition already exists (same variables), link to existing definition to avoid too many SimConnect definition
+ addDefinition := TAddDefinitionValue.Create(ADefinition, ADataHandler);
+ Client.Comm.Send(TM_ADDDEFINITION, addDefinition);
+
+ addDefinition.WaitFor(INFINITE);
+ Result := addDefinition.DefinitionID;
end;
-procedure TFSXSimConnectInterface.RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler);
+procedure TFSXSimConnectInterface.RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
+var
+ removeDefinition: TRemoveDefinitionValue;
+
begin
- Client.Comm.Send(TM_REMOVEDEFINITION, ADataHandler);
+ removeDefinition := TRemoveDefinitionValue.Create(ADefinitionID, ADataHandler);
+ Client.Comm.Send(TM_REMOVEDEFINITION, removeDefinition);
+
+ removeDefinition.WaitFor(INFINITE);
end;
{ TFSXSimConnectDefinition }
-constructor TFSXSimConnectDefinition.Create(ASimConnect: IFSXSimConnect);
+constructor TFSXSimConnectDefinition.Create;
begin
+ inherited Create;
+ FVariables := TFSXSimConnectVariableList.Create(True);
end;
-procedure TFSXSimConnectDefinition.AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single);
+destructor TFSXSimConnectDefinition.Destroy;
begin
+ FreeAndNil(FVariables);
+ inherited Destroy;
end;
-procedure TFSXSimConnectDefinition.Apply(ASimConnectHandle: THandle; ADefinitionID: Integer);
+procedure TFSXSimConnectDefinition.AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single);
begin
-// SimConnect_AddToDataDefinition(ASimConnectHandle, ADefinitionID,
-// AnsiString(AVariableName), AnsiString(AUnitsName), ADatumType, AEpsilon, 0);
+ Variables.Add(TFSXSimConnectVariable.Create(AVariableName, AUnitsName, ADataType, AEpsilon));
+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.
diff --git a/G940LEDControl/Units/FSXSimConnectIntf.pas b/G940LEDControl/Units/FSXSimConnectIntf.pas
index 928a9d9..8b9bae5 100644
--- a/G940LEDControl/Units/FSXSimConnectIntf.pas
+++ b/G940LEDControl/Units/FSXSimConnectIntf.pas
@@ -28,18 +28,32 @@ type
procedure Detach(AObserver: IFSXSimConnectObserver);
function CreateDefinition: IFSXSimConnectDefinition;
- procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler);
- procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler);
+ function AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer;
+ procedure RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler);
end;
IFSXSimConnectDefinition = interface
['{F1EAB3B1-0A3D-4B06-A75F-823E15C313B8}']
- procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0);
- procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer);
+ procedure AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single = 0);
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
FSX_UNIT_PERCENT = 'percent';
diff --git a/G940LEDControl/Units/G940LEDStateConsumer.pas b/G940LEDControl/Units/G940LEDStateConsumer.pas
index caf058c..9df311e 100644
--- a/G940LEDControl/Units/G940LEDStateConsumer.pas
+++ b/G940LEDControl/Units/G940LEDStateConsumer.pas
@@ -13,7 +13,9 @@ uses
const
TM_FINDTHROTTLEDEVICE = 2001;
- TM_NOTIFY_DEVICESTATE = 2002;
+ TM_TESTTHROTTLEDEVICE = 2002;
+
+ TM_NOTIFY_DEVICESTATE = 2003;
type
@@ -21,10 +23,13 @@ type
private
FDirectInput: IDirectInput8;
FThrottleDevice: IDirectInputDevice8;
+ FTHrottleDeviceGUID: TGUID;
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
function Initialize: Boolean; override;
+ procedure Cleanup; override;
procedure FindThrottleDevice;
procedure FoundThrottleDevice(ADeviceGUID: TGUID);
@@ -35,6 +40,7 @@ type
property DirectInput: IDirectInput8 read FDirectInput;
property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice;
+ property ThrottleDeviceGUID: TGUID read FTHrottleDeviceGUID;
end;
@@ -63,23 +69,6 @@ const
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;
var
vendorID: Word;
@@ -127,81 +116,13 @@ begin
end;
-{
-procedure TG940LEDStateConsumer.ResetLEDState;
+procedure TG940LEDStateConsumer.Cleanup;
begin
- FRed := 0;
- 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;
+ inherited Cleanup;
if Assigned(ThrottleDevice) then
- { Logitech SDK will not change the color outside of the main thread
- RunInMainThread(TRunInMainThreadSetLEDs.Create(ThrottleDevice, FRed, FGreen), Destroying);
+ SetLEDs(ThrottleDevice, 0, $FF);
end;
-}
procedure TG940LEDStateConsumer.FindThrottleDevice;
@@ -222,7 +143,10 @@ end;
procedure TG940LEDStateConsumer.FoundThrottleDevice(ADeviceGUID: TGUID);
begin
if DirectInput.CreateDevice(ADeviceGUID, FThrottleDevice, nil) = S_OK then
+ begin
+ FTHrottleDeviceGUID := ADeviceGUID;
SetDeviceState(DEVICESTATE_FOUND);
+ end;
end;
@@ -279,28 +203,22 @@ begin
end;
-procedure TG940LEDStateConsumer.MsgFindThrottleDevice(var msg: TOmniMessage);
+procedure TG940LEDStateConsumer.TMFindThrottleDevice(var Msg: TOmniMessage);
begin
FindThrottleDevice;
end;
-{ TRunInMainThreadSetLEDs }
-(*
-constructor TRunInMainThreadSetLEDs.Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte);
+procedure TG940LEDStateConsumer.TMTestThrottleDevice(var Msg: TOmniMessage);
begin
- inherited Create;
-
- FDevice := ADevice;
- FRed := ARed;
- FGreen := AGreen;
+ if Assigned(ThrottleDevice) then
+ begin
+ if DirectInput.GetDeviceStatus(ThrottleDeviceGUID) = DI_NOTATTACHED then
+ begin
+ FThrottleDevice := nil;
+ SetDeviceState(DEVICESTATE_NOTFOUND);
+ end;
+ end;
end;
-
-procedure TRunInMainThreadSetLEDs.Execute;
-begin
- SetLEDs(FDevice, FRed, FGreen);
-end;
-*)
-
end.
diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas
index 247ced4..98448ad 100644
--- a/G940LEDControl/Units/LEDFunction.pas
+++ b/G940LEDControl/Units/LEDFunction.pas
@@ -81,8 +81,8 @@ type
function GetCurrentState: ILEDStateWorker; virtual; abstract;
public
- constructor Create; overload;
- constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload;
+ constructor Create; overload; virtual;
+ constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload; virtual;
destructor Destroy; override;
end;
@@ -138,7 +138,7 @@ destructor TCustomMultiStateLEDFunction.Destroy;
begin
FreeAndNil(FStates);
- inherited;
+ inherited Destroy;
end;
@@ -229,6 +229,8 @@ var
begin
Result := nil;
+ if not Assigned(States) then
+ exit;
for state in States do
if (state as ICustomLEDState).GetUID = AUID then
@@ -263,7 +265,7 @@ destructor TCustomLEDFunctionProvider.Destroy;
begin
FreeAndNil(FFunctions);
- inherited;
+ inherited Destroy;
end;
diff --git a/G940LEDControl/Units/LEDFunctionRegistry.pas b/G940LEDControl/Units/LEDFunctionRegistry.pas
index c328115..a4605f7 100644
--- a/G940LEDControl/Units/LEDFunctionRegistry.pas
+++ b/G940LEDControl/Units/LEDFunctionRegistry.pas
@@ -114,7 +114,7 @@ destructor TLEDFunctionRegistry.Destroy;
begin
FreeAndNil(FProviders);
- inherited;
+ inherited Destroy;
end;
@@ -155,7 +155,7 @@ destructor TLEDFunctionProviderList.Destroy;
begin
FreeAndNil(FList);
- inherited;
+ inherited Destroy;
end;
diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas
index a791bc5..09b998a 100644
--- a/G940LEDControl/Units/LEDStateConsumer.pas
+++ b/G940LEDControl/Units/LEDStateConsumer.pas
@@ -21,13 +21,7 @@ const
type
- IRunInMainThread = interface(IOmniWaitableValue)
- ['{68B8F2F7-ED40-4078-9D99-503D7AFA068B}']
- procedure Execute;
- end;
-
-
- TLEDStateConsumer = class(TOmniWorker)
+ TLEDStateConsumer = class(TOmniWorker, ILEDFunctionObserver)
private
FButtonWorkers: TInterfaceList;
FButtonColors: TInterfaceList;
@@ -42,6 +36,9 @@ type
property ButtonColors: TInterfaceList read FButtonColors;
property HasTickTimer: Boolean read FHasTickTimer;
protected
+ { ILEDFunctionObserver }
+ procedure ObserveUpdate(Sender: ILEDFunctionWorker);
+
procedure Changed; virtual;
procedure Update; virtual; abstract;
protected
@@ -171,21 +168,49 @@ begin
end;
+procedure TLEDStateConsumer.ObserveUpdate(Sender: ILEDFunctionWorker);
+begin
+ Changed;
+end;
+
+
procedure TLEDStateConsumer.TMLoadProfile(var Msg: TOmniMessage);
var
+ oldWorkers: TInterfaceList;
+ oldWorker: IInterface;
profile: TProfile;
buttonIndex: Integer;
+ worker: ILEDFunctionWorker;
begin
profile := Msg.MsgData;
- ButtonWorkers.Clear;
- for buttonIndex := 0 to Pred(profile.ButtonCount) do
- begin
- if profile.HasButton(buttonIndex) then
- ButtonWorkers.Add(CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker)
- else
- ButtonWorkers.Add(nil);
+ { 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;
+
+ for buttonIndex := 0 to Pred(profile.ButtonCount) do
+ begin
+ if profile.HasButton(buttonIndex) then
+ begin
+ worker := CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker;
+ ButtonWorkers.Add(worker);
+
+ if Assigned(worker) then
+ worker.Attach(Self);
+ end else
+ ButtonWorkers.Add(nil);
+ end;
+ finally
+ FreeAndNil(oldWorkers);
end;
Changed;
diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas
index 66eb916..a4a1c6a 100644
--- a/G940LEDControl/Units/Profile.pas
+++ b/G940LEDControl/Units/Profile.pas
@@ -188,7 +188,7 @@ destructor TProfile.Destroy;
begin
FreeAndNil(FButtons);
- inherited;
+ inherited Destroy;
end;