diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm index 68959e6..57c71d9 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.dfm +++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm @@ -263,13 +263,14 @@ object ButtonFunctionForm: TButtonFunctionForm TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages] TreeOptions.SelectionOptions = [toFullRowSelect] OnFocusChanged = vstFunctionsFocusChanged + OnFreeNode = vstFunctionsFreeNode OnGetText = vstFunctionsGetText OnPaintText = vstFunctionsPaintText OnIncrementalSearch = vstFunctionsIncrementalSearch Columns = < item Position = 0 - Width = 253 + Width = 257 WideText = 'Available functions' end> end diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas index f1f7f10..99c77ef 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.pas +++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas @@ -15,6 +15,7 @@ uses LEDColorIntf, LEDFunctionIntf, + LEDFunctionRegistry, LEDStateIntf, Profile, Vcl.ActnList; @@ -60,6 +61,7 @@ type procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormKeyPress(Sender: TObject; var Key: Char); procedure FormShow(Sender: TObject); + procedure vstFunctionsFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); procedure vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); procedure vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure vstFunctionsIncrementalSearch(Sender: TBaseVirtualTree; Node: PVirtualNode; const SearchText: string; var Result: Integer); @@ -73,8 +75,9 @@ type FSelectedProvider: ILEDFunctionProvider; FSelectedFunction: ILEDFunction; FStateControls: TStateControlInfoList; + FFunctionRegistry: TLEDFunctionRegistry; protected - procedure Initialize(AProfile: TProfile; AButtonIndex: Integer); + procedure Initialize(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer); procedure LoadFunctions; procedure ApplyFilter(const AFilter: string); @@ -91,8 +94,9 @@ type property Profile: TProfile read FProfile; property ButtonIndex: Integer read FButtonIndex; + property FunctionRegistry: TLEDFunctionRegistry read FFunctionRegistry; public - class function Execute(AProfile: TProfile; AButtonIndex: Integer): Boolean; + class function Execute(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer): Boolean; end; @@ -118,7 +122,6 @@ uses System.SysUtils, Winapi.Windows, - LEDFunctionRegistry, LEDResources; @@ -150,11 +153,11 @@ const { TButtonFunctionForm } -class function TButtonFunctionForm.Execute(AProfile: TProfile; AButtonIndex: Integer): Boolean; +class function TButtonFunctionForm.Execute(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer): Boolean; begin with Self.Create(nil) do try - Initialize(AProfile, AButtonIndex); + Initialize(AFunctionRegistry, AProfile, AButtonIndex); Result := (ShowModal = mrOk); finally Free; @@ -247,7 +250,7 @@ begin categoryNodes := TDictionary.Create; try - for provider in TLEDFunctionRegistry.Providers do + for provider in FunctionRegistry.Providers do begin isCurrentProvider := Assigned(CurrentProvider) and (provider.GetUID = CurrentProvider.GetUID); @@ -371,8 +374,9 @@ begin end; -procedure TButtonFunctionForm.Initialize(AProfile: TProfile; AButtonIndex: Integer); +procedure TButtonFunctionForm.Initialize(AFunctionRegistry: TLEDFunctionRegistry; AProfile: TProfile; AButtonIndex: Integer); begin + FFunctionRegistry := AFunctionRegistry; FProfile := AProfile; FButtonIndex := AButtonIndex; FButton := nil; @@ -384,7 +388,7 @@ begin if Profile.HasButton(ButtonIndex) then begin FButton := Profile.Buttons[ButtonIndex]; - FCurrentProvider := TLEDFunctionRegistry.Find(Button.ProviderUID); + FCurrentProvider := FunctionRegistry.Find(Button.ProviderUID); if Assigned(CurrentProvider) then FCurrentFunction := CurrentProvider.Find(Button.FunctionUID); @@ -519,6 +523,16 @@ begin end; +procedure TButtonFunctionForm.vstFunctionsFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); +var + nodeData: PFunctionNodeData; + +begin + nodeData := Sender.GetNodeData(Node); + Finalize(nodeData^); +end; + + procedure TButtonFunctionForm.vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); var nodeData: PFunctionNodeData; diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index 5f6f0eb..18e5eff 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -24,6 +24,7 @@ uses X2UtPersistIntf, ControlIntf, + LEDFunctionRegistry, FSXSimConnectIntf, LEDStateConsumer, Profile, @@ -163,6 +164,8 @@ type FSettingsFileName: string; FSettings: TSettings; FLoadingSettings: Boolean; + + FFunctionRegistry: TLEDFunctionRegistry; protected procedure RegisterDeviceArrival; procedure UnregisterDeviceArrival; @@ -219,6 +222,7 @@ type property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; property Log: IX2Log read FLog; + property FunctionRegistry: TLEDFunctionRegistry read FFunctionRegistry; end; @@ -249,7 +253,6 @@ uses G940LEDStateConsumer, LEDColorIntf, LEDFunctionIntf, - LEDFunctionRegistry, StaticLEDFunction, StaticResources; @@ -324,19 +327,20 @@ begin SetFSXState(TextFSXDisconnected, False); - TLEDFunctionRegistry.Register(TStaticLEDFunctionProvider.Create); + FFunctionRegistry := TLEDFunctionRegistry.Create; + FunctionRegistry.Register(TStaticLEDFunctionProvider.Create); SetLength(scriptPaths, 2); scriptPaths[0] := App.Path + FSXScriptsPath; scriptPaths[1] := App.UserPath + UserDataPath + FSXScriptsPath; - TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create(scriptPaths)); + FunctionRegistry.Register(TFSXLEDFunctionProvider.Create(scriptPaths)); FEventMonitor := TOmniEventMonitor.Create(Self); Log.Info('Starting G940 LED state consumer thread'); - worker := TG940LEDStateConsumer.Create(TX2GlobalLog.Category('G940 LED state consumer')); + worker := TG940LEDStateConsumer.Create(TX2GlobalLog.Category('G940 LED state consumer'), FunctionRegistry); FStateConsumerTask := EventMonitor.Monitor(CreateTask(worker)); EventMonitor.OnTaskMessage := EventMonitorMessage; @@ -379,6 +383,14 @@ begin TX2LogObserverMonitorForm.CloseInstance(TX2GlobalLog.Instance); TX2LogObserverMonitorForm.UnlockInstance(TX2GlobalLog.Instance); + + if Assigned(StateConsumerTask) then + begin + StateConsumerTask.Stop; + StateConsumerTask.WaitFor(INFINITE); + end; + + FreeAndNil(FFunctionRegistry); end; @@ -726,7 +738,7 @@ begin end; buttonFunction := nil; - provider := TLEDFunctionRegistry.Find(providerUID); + provider := FunctionRegistry.Find(providerUID); if Assigned(provider) then buttonFunction := provider.Find(functionUID); @@ -925,7 +937,7 @@ begin end; buttonIndex := (Sender as TComponent).Tag; - if TButtonFunctionForm.Execute(profile, buttonIndex) then + if TButtonFunctionForm.Execute(FunctionRegistry, profile, buttonIndex) then begin if newProfile then AddProfile(profile); @@ -1308,7 +1320,7 @@ var fsxProvider: IFSXLEDFunctionProvider; begin - if Supports(TLEDFunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then + if Supports(FunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then fsxProvider.SetProfileMenu(Settings.ProfileMenu, Settings.ProfileMenuCascaded); end; @@ -1318,7 +1330,7 @@ var fsxProvider: IFSXLEDFunctionProvider; begin - if Supports(TLEDFunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then + if Supports(FunctionRegistry.Find(FSXProviderUID), IFSXLEDFunctionProvider, fsxProvider) then fsxProvider.SetProfileMenu(False, False); end; diff --git a/G940LEDControl/Units/FSXLEDFunctionProvider.pas b/G940LEDControl/Units/FSXLEDFunctionProvider.pas index 6c24f19..4f0275e 100644 --- a/G940LEDControl/Units/FSXLEDFunctionProvider.pas +++ b/G940LEDControl/Units/FSXLEDFunctionProvider.pas @@ -181,10 +181,10 @@ end; destructor TFSXLEDFunctionProvider.Destroy; begin + inherited Destroy; + FreeAndNil(FScriptSimConnect); FreeAndNil(FSimConnectLock); - - inherited Destroy; end; diff --git a/G940LEDControl/Units/LEDFunctionRegistry.pas b/G940LEDControl/Units/LEDFunctionRegistry.pas index a4605f7..0b7942b 100644 --- a/G940LEDControl/Units/LEDFunctionRegistry.pas +++ b/G940LEDControl/Units/LEDFunctionRegistry.pas @@ -13,24 +13,16 @@ type TLEDFunctionRegistry = class(TObject) private FProviders: TLEDFunctionProviderList; - protected - class function Instance: TLEDFunctionRegistry; - - procedure DoRegister(AProvider: ILEDFunctionProvider); - procedure DoUnregister(AProvider: ILEDFunctionProvider); - function DoFind(const AUID: string): ILEDFunctionProvider; - - function GetProviders: TLEDFunctionProviderList; public constructor Create; destructor Destroy; override; - class procedure Register(AProvider: ILEDFunctionProvider); - class procedure Unregister(AProvider: ILEDFunctionProvider); + procedure Register(AProvider: ILEDFunctionProvider); + procedure Unregister(AProvider: ILEDFunctionProvider); - class function Find(const AUID: string): ILEDFunctionProvider; + function Find(const AUID: string): ILEDFunctionProvider; - class function Providers: TLEDFunctionProviderList; + function Providers: TLEDFunctionProviderList; end; @@ -64,44 +56,7 @@ uses SysUtils; -var - RegistryInstance: TLEDFunctionRegistry; - - { TLEDFunctionRegistry } -class procedure TLEDFunctionRegistry.Register(AProvider: ILEDFunctionProvider); -begin - Instance.DoRegister(AProvider); -end; - - -class procedure TLEDFunctionRegistry.Unregister(AProvider: ILEDFunctionProvider); -begin - Instance.DoUnregister(AProvider); -end; - - -class function TLEDFunctionRegistry.Find(const AUID: string): ILEDFunctionProvider; -begin - Result := Instance.DoFind(AUID); -end; - - -class function TLEDFunctionRegistry.Providers: TLEDFunctionProviderList; -begin - Result := Instance.GetProviders; -end; - - -class function TLEDFunctionRegistry.Instance: TLEDFunctionRegistry; -begin - if not Assigned(RegistryInstance) then - RegistryInstance := TLEDFunctionRegistry.Create; - - Result := RegistryInstance; -end; - - constructor TLEDFunctionRegistry.Create; begin inherited Create; @@ -118,25 +73,25 @@ begin end; -procedure TLEDFunctionRegistry.DoRegister(AProvider: ILEDFunctionProvider); +procedure TLEDFunctionRegistry.Register(AProvider: ILEDFunctionProvider); begin FProviders.Add(AProvider); end; -procedure TLEDFunctionRegistry.DoUnregister(AProvider: ILEDFunctionProvider); +procedure TLEDFunctionRegistry.Unregister(AProvider: ILEDFunctionProvider); begin FProviders.Remove(AProvider); end; -function TLEDFunctionRegistry.DoFind(const AUID: string): ILEDFunctionProvider; +function TLEDFunctionRegistry.Find(const AUID: string): ILEDFunctionProvider; begin Result := FProviders.Find(AUID); end; -function TLEDFunctionRegistry.GetProviders: TLEDFunctionProviderList; +function TLEDFunctionRegistry.Providers: TLEDFunctionProviderList; begin Result := FProviders; end; @@ -208,9 +163,4 @@ begin Result := ((inherited GetCurrent) as ILEDFunctionProvider); end; - -initialization -finalization - FreeAndNil(RegistryInstance); - end. diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index 3f44ec4..f1d4d7a 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -11,6 +11,7 @@ uses LEDColorIntf, LEDFunctionIntf, + LEDFunctionRegistry, Profile; @@ -28,6 +29,7 @@ type FButtonColors: TInterfaceList; FHasTickTimer: Boolean; FLog: IX2Log; + FFunctionRegistry: TLEDFunctionRegistry; protected function Initialize: Boolean; override; procedure Cleanup; override; @@ -45,11 +47,12 @@ type procedure Update; virtual; abstract; property Log: IX2Log read FLog; + property FunctionRegistry: TLEDFunctionRegistry read FFunctionRegistry; protected procedure TMLoadProfile(var Msg: TOmniMessage); message TM_LOADPROFILE; procedure TMTick(var Msg: TOmniMessage); message TM_TICK; public - constructor Create(ALog: IX2Log); + constructor Create(ALog: IX2Log; AFunctionRegistry: TLEDFunctionRegistry); end; @@ -59,7 +62,6 @@ uses System.SysUtils, Winapi.Windows, - LEDFunctionRegistry, LEDStateIntf; @@ -82,11 +84,12 @@ type { TLEDStateConsumer } -constructor TLEDStateConsumer.Create(ALog: IX2Log); +constructor TLEDStateConsumer.Create(ALog: IX2Log; AFunctionRegistry: TLEDFunctionRegistry); begin inherited Create; FLog := ALog; + FFunctionRegistry := AFunctionRegistry; end; @@ -118,7 +121,7 @@ var begin Result := nil; - provider := TLEDFunctionRegistry.Find(AProfileButton.ProviderUID); + provider := FunctionRegistry.Find(AProfileButton.ProviderUID); if Assigned(provider) then begin ledFunction := provider.Find(AProfileButton.FunctionUID); diff --git a/G940LEDControl/Units/LuaLEDFunctionProvider.pas b/G940LEDControl/Units/LuaLEDFunctionProvider.pas index fdde0bf..9803884 100644 --- a/G940LEDControl/Units/LuaLEDFunctionProvider.pas +++ b/G940LEDControl/Units/LuaLEDFunctionProvider.pas @@ -6,6 +6,8 @@ uses System.SysUtils, System.Types, + X2Log.Intf, + LEDFunction, LEDFunctionIntf, LEDStateIntf, @@ -50,6 +52,12 @@ type protected function CreateLuaLEDFunction(AInfo: ILuaTable; ASetup: ILuaFunction): TCustomLuaLEDFunction; virtual; abstract; + procedure AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable); + procedure AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable); + + procedure DoLog(AContext: ILuaContext; ALevel: TX2LogLevel); + procedure DoLogMessage(AContext: ILuaContext; ALevel: TX2LogLevel; const AMessage: string); + procedure ScriptRegisterFunction(Context: ILuaContext); procedure ScriptSetState(Context: ILuaContext); @@ -91,7 +99,6 @@ uses System.IOUtils, Lua.API, - X2Log.Intf, X2Log.Global, LEDColorIntf, @@ -99,18 +106,15 @@ uses type + TLuaLogProc = reference to procedure(AContext: ILuaContext; ALevel: TX2LogLevel); + TLuaLog = class(TPersistent) private - FInterpreter: TLua; + FOnLog: TLuaLogProc; protected - procedure AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable); - procedure AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable); - - procedure Log(AContext: ILuaContext; ALevel: TX2LogLevel); - - property Interpreter: TLua read FInterpreter; + property OnLog: TLuaLogProc read FOnLog; public - constructor Create(AInterpreter: TLua); + constructor Create(AOnLog: TLuaLogProc); published procedure Verbose(Context: ILuaContext); procedure Info(Context: ILuaContext); @@ -149,7 +153,7 @@ begin FWorkers := TDictionary.Create; FInterpreter := TLua.Create; FScriptFolders := AScriptFolders; - FScriptLog := TLuaLog.Create(FInterpreter); + FScriptLog := TLuaLog.Create(DoLog); InitInterpreter; @@ -159,11 +163,11 @@ end; destructor TCustomLuaLEDFunctionProvider.Destroy; begin + inherited Destroy; + FreeAndNil(FInterpreter); FreeAndNil(FScriptLog); FreeAndNil(FWorkers); - - inherited Destroy; end; @@ -189,6 +193,87 @@ begin end; +procedure TCustomLuaLEDFunctionProvider.AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable); +begin + case AVariable.VariableType of + VariableBoolean: + if AVariable.AsBoolean then + ABuilder.Append('true') + else + ABuilder.Append('false'); + + VariableTable: + AppendTable(ABuilder, AVariable.AsTable); + else + ABuilder.Append(AVariable.AsString); + end; +end; + + +procedure TCustomLuaLEDFunctionProvider.AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable); +var + firstItem: Boolean; + item: TLuaKeyValuePair; + +begin + ABuilder.Append('{ '); + firstItem := True; + + for item in ATable do + begin + if firstItem then + firstItem := False + else + ABuilder.Append(', '); + + AppendVariable(ABuilder, item.Key); + ABuilder.Append(' = '); + AppendVariable(ABuilder, item.Value); + end; + + ABuilder.Append(' }'); +end; + + +procedure TCustomLuaLEDFunctionProvider.DoLog(AContext: ILuaContext; ALevel: TX2LogLevel); +var + msg: TStringBuilder; + parameter: ILuaVariable; + +begin + msg := TStringBuilder.Create; + try + for parameter in AContext.Parameters do + begin + AppendVariable(msg, parameter); + msg.Append(' '); + end; + + DoLogMessage(AContext, ALevel, msg.ToString); + finally + FreeAndNil(msg); + end; +end; + + +procedure TCustomLuaLEDFunctionProvider.DoLogMessage(AContext: ILuaContext; ALevel: TX2LogLevel; const AMessage: string); +var + debug: lua_Debug; + fileName: string; + +begin + fileName := 'Lua'; + + if Interpreter.HasState and (lua_getstack(Interpreter.State, 1, debug) <> 0) then + begin + lua_getinfo(Interpreter.State, 'Sl', debug); + fileName := fileName + ' - ' + string(debug.source) + end; + + TX2GlobalLog.Log(ALevel, AMessage, filename); +end; + + procedure TCustomLuaLEDFunctionProvider.ScriptRegisterFunction(Context: ILuaContext); var info: ILuaTable; @@ -210,6 +295,7 @@ begin if not info.HasValue('uid') then raise ELuaScriptError.Create('"uid" value is required for RegisterFunction parameter 1'); + DoLogMessage(Context, TX2LogLevel.Info, Format('Registering function: %s', [info.GetValue('uid').AsString])); RegisterFunction(CreateLuaLEDFunction(info, setup)); end; @@ -237,6 +323,7 @@ begin if not Assigned(worker) then raise ELuaScriptError.Create('Context expected for SetState parameter 1'); + DoLogMessage(Context, TX2LogLevel.Info, Format('Setting state for %s to: %s', [worker.GetFunctionUID, stateUID])); worker.SetCurrentState(stateUID); end; @@ -397,108 +484,35 @@ end; { TLuaLog } -constructor TLuaLog.Create(AInterpreter: TLua); +constructor TLuaLog.Create(AOnLog: TLuaLogProc); begin inherited Create; - FInterpreter := AInterpreter; -end; - - -procedure TLuaLog.AppendVariable(ABuilder: TStringBuilder; AVariable: ILuaVariable); -begin - case AVariable.VariableType of - VariableBoolean: - if AVariable.AsBoolean then - ABuilder.Append('true') - else - ABuilder.Append('false'); - - VariableTable: - AppendTable(ABuilder, AVariable.AsTable); - else - ABuilder.Append(AVariable.AsString); - end; -end; - - -procedure TLuaLog.AppendTable(ABuilder: TStringBuilder; ATable: ILuaTable); -var - firstItem: Boolean; - item: TLuaKeyValuePair; - -begin - ABuilder.Append('{ '); - firstItem := True; - - for item in ATable do - begin - if firstItem then - firstItem := False - else - ABuilder.Append(', '); - - AppendVariable(ABuilder, item.Key); - ABuilder.Append(' = '); - AppendVariable(ABuilder, item.Value); - end; - - ABuilder.Append(' }'); -end; - - -procedure TLuaLog.Log(AContext: ILuaContext; ALevel: TX2LogLevel); -var - debug: lua_Debug; - fileName: string; - msg: TStringBuilder; - parameter: ILuaVariable; - -begin - fileName := 'Lua'; - - if Interpreter.HasState and (lua_getstack(Interpreter.State, 1, debug) <> 0) then - begin - lua_getinfo(Interpreter.State, 'Sl', debug); - fileName := fileName + ' - ' + string(debug.source) - end; - - msg := TStringBuilder.Create; - try - for parameter in AContext.Parameters do - begin - AppendVariable(msg, parameter); - msg.Append(' '); - end; - - TX2GlobalLog.Log(ALevel, msg.ToString, fileName); - finally - FreeAndNil(msg); - end; + FOnLog := AOnLog; end; procedure TLuaLog.Verbose(Context: ILuaContext); begin - Log(Context, TX2LogLevel.Verbose); + OnLog(Context, TX2LogLevel.Verbose); end; procedure TLuaLog.Info(Context: ILuaContext); begin - Log(Context, TX2LogLevel.Info); + OnLog(Context, TX2LogLevel.Info); end; procedure TLuaLog.Warning(Context: ILuaContext); begin - Log(Context, TX2LogLevel.Warning); + OnLog(Context, TX2LogLevel.Warning); end; procedure TLuaLog.Error(Context: ILuaContext); begin - Log(Context, TX2LogLevel.Error); + OnLog(Context, TX2LogLevel.Error); end; end.