diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index 928190e..4bff79c 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -15,7 +15,9 @@ uses OtlTaskControl, pngimage, - CustomLEDStateProvider; + LEDFunctionMap, + LEDStateConsumer, + LEDStateProvider; type @@ -59,10 +61,7 @@ type // procedure tmrG940InitTimer(Sender: TObject); private FEventMonitor: TOmniEventMonitor; - FProviderConsumerChannel: IOmniTwoWayChannel; FStateConsumerTask: IOmniTaskControl; - FStateProviderWorker: IOmniWorker; - FStateProviderTask: IOmniTaskControl; // FInitCounter: Integer; // FInitRedState: Byte; @@ -70,20 +69,17 @@ type protected procedure SetDeviceState(const AMessage: string; AFound: Boolean); - procedure InitializeStateProvider(AProviderClass: TCustomLEDStateProviderClass); + procedure InitializeStateProvider(AProviderClass: TLEDStateProviderClass); procedure FinalizeStateProvider; procedure UpdateMapping; - procedure UpdateMappingFSX(AFunctionMap: TLEDFunctionMap); + procedure UpdateMappingFSX; procedure EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage); procedure HandleDeviceStateMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage); property EventMonitor: TOmniEventMonitor read FEventMonitor; - property ProviderConsumerChannel: IOmniTwoWayChannel read FProviderConsumerChannel; property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; - property StateProviderWorker: IOmniWorker read FStateProviderWorker; - property StateProviderTask: IOmniTaskControl read FStateProviderTask; end; @@ -96,7 +92,7 @@ uses OtlTask, FSXLEDStateProvider, - LEDStateConsumer; + G940LEDStateConsumer; {$R *.dfm} @@ -122,10 +118,9 @@ var begin FEventMonitor := TOmniEventMonitor.Create(Self); - FProviderConsumerChannel := CreateTwoWayChannel(1024); - consumer := TG940LEDStateConsumer.Create(ProviderConsumerChannel.Endpoint1); - FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)); + consumer := TG940LEDStateConsumer.Create; + FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait; // ToDo handle OnTerminate, check exit code for initialization errors EventMonitor.OnTaskMessage := EventMonitorMessage; @@ -201,55 +196,32 @@ end; *) -procedure TMainForm.InitializeStateProvider(AProviderClass: TCustomLEDStateProviderClass); -begin - if Assigned(StateProviderTask) then - FinalizeStateProvider; - - FStateProviderWorker := AProviderClass.Create(ProviderConsumerChannel.Endpoint2); - FStateProviderTask := CreateTask(StateProviderWorker); - - StateProviderTask.Run; - StateProviderTask.WaitForInit; - +procedure TMainForm.InitializeStateProvider(AProviderClass: TLEDStateProviderClass); +begin UpdateMapping; + TLEDStateConsumer.InitializeStateProvider(StateConsumerTask, AProviderClass); end; procedure TMainForm.FinalizeStateProvider; begin - FStateProviderWorker := nil; - - if Assigned(StateProviderTask) then - StateProviderTask.Terminate; - - FStateProviderTask := nil; + TLEDStateConsumer.FinalizeStateProvider(StateConsumerTask); end; procedure TMainForm.UpdateMapping; -var - provider: TCustomLEDStateProvider; - functionMap: TLEDFunctionMap; - begin - if not Assigned(StateProviderWorker) then + if not Assigned(StateConsumerTask) then Exit; - provider := (StateProviderWorker.Implementor as TCustomLEDStateProvider); - functionMap := provider.LockFunctionMap; - try - UpdateMappingFSX(functionMap); - finally - provider.UnlockFunctionMap; - end; + TLEDStateConsumer.ClearFunctions(StateConsumerTask); + UpdateMappingFSX; end; -procedure TMainForm.UpdateMappingFSX(AFunctionMap: TLEDFunctionMap); +procedure TMainForm.UpdateMappingFSX; begin - AFunctionMap.Clear; - AFunctionMap.SetFunction(4, FUNCTION_FSX_GEAR); + TLEDStateConsumer.SetFunction(StateConsumerTask, 3, FUNCTION_FSX_GEAR); end; @@ -279,24 +251,6 @@ begin end; -(* -procedure TMainForm.DoStateChange(Sender: TObject; ALEDPosition: Integer; AState: TLEDState); -begin - if not Assigned(ThrottleDevice) then - exit; - - // (MvR) 2-1-2012: dit moet slimmer zodra we lsWarning/lsError willen ondersteunen - - case AState of - lsOff: SetButtonColor(ThrottleDevice, TLogiPanelButton(Pred(ALEDPosition)), LOGI_OFF); - lsGreen: SetButtonColor(ThrottleDevice, TLogiPanelButton(Pred(ALEDPosition)), LOGI_GREEN); - lsAmber: SetButtonColor(ThrottleDevice, TLogiPanelButton(Pred(ALEDPosition)), LOGI_AMBER); - lsRed: SetButtonColor(ThrottleDevice, TLogiPanelButton(Pred(ALEDPosition)), LOGI_RED); - end; -end; -*) - - procedure TMainForm.btnFSXConnectClick(Sender: TObject); begin InitializeStateProvider(TFSXLEDStateProvider); diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 117f33c..afd24b9 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -3,9 +3,11 @@ program G940LEDControl; uses Forms, MainFrm in 'Forms\MainFrm.pas' {MainForm}, - CustomLEDStateProvider in 'Units\CustomLEDStateProvider.pas', FSXLEDStateProvider in 'Units\FSXLEDStateProvider.pas', - LEDStateConsumer in 'Units\LEDStateConsumer.pas'; + LEDStateConsumer in 'Units\LEDStateConsumer.pas', + LEDStateProvider in 'Units\LEDStateProvider.pas', + LEDFunctionMap in 'Units\LEDFunctionMap.pas', + G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 210224b..29bf0e4 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -33,106 +33,7 @@ Delphi.Personality -FalseTrueFalseFalseFalse1000FalseFalseFalseFalseFalse104312521.0.0.01.0.0.0G940LEDControl.dpr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +FalseTrueFalseFalseFalse1000FalseFalseFalseFalseFalse104312521.0.0.01.0.0.0G940LEDControl.dpr @@ -508,8 +409,7 @@ WPViewPDF - PDFViewClass madExceptVcl 2.0c - www.madshi.net ExpressPivotGrid 2 OLAP by Developer Express Inc. - - + @@ -519,8 +419,10 @@
MainForm
- + + +
\ No newline at end of file diff --git a/G940LEDControl/Units/CustomLEDStateProvider.pas b/G940LEDControl/Units/CustomLEDStateProvider.pas deleted file mode 100644 index 55de30b..0000000 --- a/G940LEDControl/Units/CustomLEDStateProvider.pas +++ /dev/null @@ -1,158 +0,0 @@ -unit CustomLEDStateProvider; - -interface -uses - Classes, - SyncObjs, - - OtlComm, - OtlTaskControl; - -const - MAX_LEDS = 8; - FUNCTION_NONE = 0; - - - MSG_EXECUTE = 10; - MSG_UPDATEFUNCTIONMAP = 11; - MSG_SETSTATEBYFUNCTION = 12; - - -type - TLEDState = (lsOff, lsGreen, lsAmber, lsRed, lsWarning, lsError); - - - // todo implement assign - TLEDFunctionMap = class(TPersistent) - private - FFunctions: array[0..MAX_LEDS - 1] of Integer; - - function GetCount: Integer; - public - procedure Clear; - - procedure SetFunction(ALEDIndex, AFunction: Integer); - function GetFunction(ALEDIndex: Integer): Integer; - - property Count: Integer read GetCount; - end; - - - TCustomLEDStateProvider = class(TOmniWorker) - private - FConsumerChannel: IOmniCommunicationEndpoint; - FFunctionMap: TLEDFunctionMap; - FFunctionMapLock: TCriticalSection; - FState: array[0..MAX_LEDS - 1] of TLEDState; - protected - procedure MsgExecute(var msg: TOmniMessage); message MSG_EXECUTE; - protected - function Initialize: Boolean; override; - procedure Execute; virtual; abstract; - - procedure SetStateByFunction(AFunction: Integer; AState: TLEDState); - - property ConsumerChannel: IOmniCommunicationEndpoint read FConsumerChannel; - public - constructor Create(AConsumerChannel: IOmniCommunicationEndpoint); - destructor Destroy; override; - - function LockFunctionMap: TLEDFunctionMap; - procedure UnlockFunctionMap; - end; - - TCustomLEDStateProviderClass = class of TCustomLEDStateProvider; - - -implementation -uses - SysUtils; - - -{ TCustomLEDStateProvider } -constructor TCustomLEDStateProvider.Create(AConsumerChannel: IOmniCommunicationEndpoint); -var - ledIndex: Integer; - -begin - inherited Create; - - FConsumerChannel := AConsumerChannel; - - FFunctionMap := TLEDFunctionMap.Create; - FFunctionMapLock := TCriticalSection.Create; - - for ledIndex := Low(FState) to High(FState) do - FState[ledIndex] := lsGreen; -end; - - -destructor TCustomLEDStateProvider.Destroy; -begin - FreeAndNil(FFunctionMap); - FreeAndNil(FFunctionMapLock); - - inherited; -end; - -function TCustomLEDStateProvider.Initialize: Boolean; -begin - Result := True; - Task.Comm.OtherEndpoint.Send(MSG_EXECUTE); -end; - -function TCustomLEDStateProvider.LockFunctionMap: TLEDFunctionMap; -begin - FFunctionMapLock.Acquire; - Result := FFunctionMap; -end; - - -procedure TCustomLEDStateProvider.UnlockFunctionMap; -begin - FFunctionMapLock.Release; - Task.Comm.Send(MSG_UPDATEFUNCTIONMAP, Self); -end; - - -procedure TCustomLEDStateProvider.SetStateByFunction(AFunction: Integer; AState: TLEDState); -begin - ConsumerChannel.Send(MSG_SETSTATEBYFUNCTION, [AFunction, Ord(AState)]); -end; - - -procedure TCustomLEDStateProvider.MsgExecute(var msg: TOmniMessage); -begin - Execute; -end; - - -{ TLEDFunctionMap } -procedure TLEDFunctionMap.Clear; -var - ledPosition: Integer; - -begin - for ledPosition := Low(FFunctions) to High(FFunctions) do - FFunctions[ledPosition] := FUNCTION_NONE; -end; - - -procedure TLEDFunctionMap.SetFunction(ALEDIndex, AFunction: Integer); -begin - FFunctions[ALEDIndex] := AFunction; -end; - - -function TLEDFunctionMap.GetFunction(ALEDIndex: Integer): Integer; -begin - Result := FFunctions[ALEDIndex]; -end; - - -function TLEDFunctionMap.GetCount: Integer; -begin - Result := Length(FFunctions); -end; - -end. diff --git a/G940LEDControl/Units/FSXLEDStateProvider.pas b/G940LEDControl/Units/FSXLEDStateProvider.pas index 3b7e876..e5da18f 100644 --- a/G940LEDControl/Units/FSXLEDStateProvider.pas +++ b/G940LEDControl/Units/FSXLEDStateProvider.pas @@ -4,8 +4,9 @@ interface uses Classes, SyncObjs, - - CustomLEDStateProvider, + + LEDStateConsumer, + LEDStateProvider, SimConnect; @@ -17,25 +18,33 @@ const type - TFSXLEDStateProvider = class(TCustomLEDStateProvider) + TFSXLEDStateProvider = class(TLEDStateProvider) private FSimConnectHandle: THandle; + FUseFunctionGear: Boolean; protected - procedure Execute; override; + function GetProcessMessagesInterval: Integer; override; - procedure UpdateMap(AConnection: THandle); + procedure SetInitialState; + procedure UpdateMap; procedure HandleDispatch(AData: PSimConnectRecv); function GetDataDouble(var AData: Cardinal): Double; property SimConnectHandle: THandle read FSimConnectHandle; + public + procedure Initialize; override; + procedure Finalize; override; + procedure ProcessMessages; override; end; implementation uses ComObj, - SysUtils; + SysUtils, + + LEDFunctionMap; const @@ -52,80 +61,73 @@ const { TFSXLEDStateProvider } -procedure TFSXLEDStateProvider.Execute; -var - connection: THandle; - data: PSimConnectRecv; - dataSize: Cardinal; - +procedure TFSXLEDStateProvider.Initialize; begin if not InitSimConnect then + raise EInitializeError.Create('SimConnect.dll could not be loaded', EXIT_ERROR_INITSIMCONNECT); + + if SimConnect_Open(FSimConnectHandle, APPNAME, 0, 0, 0, 0) <> S_OK then + raise EInitializeError.Create('Connection to Flight Simulator could not be established', EXIT_ERROR_CONNECT); + + UpdateMap; + SetInitialState; +end; + + +procedure TFSXLEDStateProvider.Finalize; +begin + inherited; + + if SimConnectHandle <> 0 then begin - Task.SetExitStatus(EXIT_ERROR_INITSIMCONNECT, 'SimConnect.dll could not be loaded'); - Task.Terminate; - exit; - end; - - if SimConnect_Open(connection, APPNAME, 0, 0, 0, 0) = S_OK then - try - UpdateMap(connection); - - while not Task.Terminated do - begin - if SimConnect_GetNextDispatch(connection, data, dataSize) = S_OK then - HandleDispatch(data); - - ProcessMessages; - Sleep(1); - end; - finally - SimConnect_Close(connection); - end else - begin - Task.SetExitStatus(EXIT_ERROR_CONNECT, 'Connection to Flight Simulator could not be established'); - Task.Terminate; + SimConnect_Close(SimConnectHandle); + FSimConnectHandle := 0; end; end; -procedure TFSXLEDStateProvider.UpdateMap(AConnection: THandle); +procedure TFSXLEDStateProvider.ProcessMessages; var - functionMap: TLEDFunctionMap; + data: PSimConnectRecv; + dataSize: Cardinal; - function HasFunction(AFunction: Integer): Boolean; - var - ledIndex: Integer; - - begin - Result := False; - - for ledIndex := 0 to Pred(functionMap.Count) do - if functionMap.GetFunction(ledIndex) = AFunction then - begin - Result := True; - break; - end; - end; - - begin - SimConnect_ClearDataDefinition(AConnection, DEFINITION_GEAR); + inherited; - functionMap := LockFunctionMap; - try - if HasFunction(FUNCTION_FSX_GEAR) then - begin - SimConnect_AddToDataDefinition(AConnection, DEFINITION_GEAR, - FSX_VARIABLE_GEARTOTALPCTEXTENDED, - FSX_UNIT_PERCENT); - SimConnect_RequestDataOnSimObject(AConnection, REQUEST_GEAR, - DEFINITION_GEAR, - SIMCONNECT_OBJECT_ID_USER, - SIMCONNECT_PERIOD_SIM_FRAME, - SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - end; - finally - UnlockFunctionMap; + while SimConnect_GetNextDispatch(SimConnectHandle, data, dataSize) = S_OK do + HandleDispatch(data); +end; + + +procedure TFSXLEDStateProvider.SetInitialState; +begin +// if FUseFunctionGear then +// begin +// SimConnect_RequestDataOnSimObject(SimConnectHandle, REQUEST_GEAR, +// DEFINITION_GEAR, +// SIMCONNECT_OBJECT_ID_USER, +// SIMCONNECT_PERIOD_ONCE, +// SIMCONNECT_DATA_REQUEST_FLAG_DEFAULT); +// end; +end; + + +procedure TFSXLEDStateProvider.UpdateMap; +begin + if FUseFunctionGear then + SimConnect_ClearDataDefinition(SimConnectHandle, DEFINITION_GEAR); + + FUseFunctionGear := Consumer.FunctionMap.HasFunction(FUNCTION_FSX_GEAR); + if FUseFunctionGear then + begin + SimConnect_AddToDataDefinition(SimConnectHandle, DEFINITION_GEAR, + FSX_VARIABLE_GEARTOTALPCTEXTENDED, + FSX_UNIT_PERCENT); + SimConnect_RequestDataOnSimObject(SimConnectHandle, REQUEST_GEAR, + DEFINITION_GEAR, + SIMCONNECT_OBJECT_ID_USER, + SIMCONNECT_PERIOD_SECOND, + SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); end; end; @@ -144,16 +146,16 @@ begin REQUEST_GEAR: begin case Trunc(GetDataDouble(simObjectData^.dwData) * 100) of - 0: SetStateByFunction(FUNCTION_FSX_GEAR, lsRed); - 100: SetStateByFunction(FUNCTION_FSX_GEAR, lsGreen); - else SetStateByFunction(FUNCTION_FSX_GEAR, lsAmber); + 0: Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsRed); + 100: Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsGreen); + else Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsAmber); end; end; end; end; SIMCONNECT_RECV_ID_QUIT: - Task.Terminate; + Terminate; end; end; @@ -163,4 +165,10 @@ begin Result := PDouble(@AData)^; end; + +function TFSXLEDStateProvider.GetProcessMessagesInterval: Integer; +begin + Result := 50; +end; + end. diff --git a/G940LEDControl/Units/G940LEDStateConsumer.pas b/G940LEDControl/Units/G940LEDStateConsumer.pas new file mode 100644 index 0000000..7f46f71 --- /dev/null +++ b/G940LEDControl/Units/G940LEDStateConsumer.pas @@ -0,0 +1,190 @@ +unit G940LEDStateConsumer; + +interface +uses + Classes, + + DirectInput, + OtlComm, + OtlTaskControl, + + LEDFunctionMap, + LEDStateConsumer; + + +const + MSG_FINDTHROTTLEDEVICE = MSG_CONSUMER_OFFSET + 1; + + +type + TG940LEDStateConsumer = class(TLEDStateConsumer) + private + FDirectInput: IDirectInput8; + FThrottleDevice: IDirectInputDevice8; + protected + procedure MsgFindThrottleDevice(var msg: TOmniMessage); message MSG_FINDTHROTTLEDEVICE; + protected + function Initialize: Boolean; override; + procedure LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); override; + + procedure FindThrottleDevice; + procedure FoundThrottleDevice(ADeviceGUID: TGUID); + + procedure SetDeviceState(AState: Integer); + + property DirectInput: IDirectInput8 read FDirectInput; + property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice; + end; + + +const + MSG_NOTIFY_DEVICESTATE = 1; + + DEVICESTATE_SEARCHING = 0; + DEVICESTATE_FOUND = 1; + DEVICESTATE_NOTFOUND = 2; + + EXIT_ERROR_LOGIJOYSTICKDLL = 1; + EXIT_ERROR_DIRECTINPUT = 2; + + +implementation +uses + SysUtils, + Windows, + + LogiJoystickDLL; + + +function EnumDevicesProc(var lpddi: TDIDeviceInstanceA; pvRef: Pointer): BOOL; stdcall; +var + vendorID: Word; + productID: Word; + +begin + Result := True; + + vendorID := LOWORD(lpddi.guidProduct.D1); + productID := HIWORD(lpddi.guidProduct.D1); + + if (vendorID = VENDOR_LOGITECH) and + (productID = PRODUCT_G940_THROTTLE) then + begin + TG940LEDStateConsumer(pvRef).FoundThrottleDevice(lpddi.guidInstance); + Result := False; + end; +end; + + + +{ TG940LEDStateConsumer } +function TG940LEDStateConsumer.Initialize: Boolean; +begin + Result := inherited Initialize; + if not Result then + exit; + + Result := False; + + if not LogiJoystickDLLInitialized then + begin + Task.SetExitStatus(EXIT_ERROR_LOGIJOYSTICKDLL, 'Could not load LogiJoystickDLL.dll'); + exit; + end; + +// btnRetry.Visible := False; +// SetState(STATE_SEARCHING, False); + + if DirectInput8Create(SysInit.HInstance, DIRECTINPUT_VERSION, IDirectInput8, FDirectInput, nil) <> S_OK then + begin + Task.SetExitStatus(EXIT_ERROR_DIRECTINPUT, 'Failed to initialize DirectInput'); + exit; + end; + + Result := True; + Task.Comm.OtherEndpoint.Send(MSG_FINDTHROTTLEDEVICE); +end; + + +procedure TG940LEDStateConsumer.LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); +var + color: TLogiColor; + +begin + // ToDo SetLEDs gebruiken (vereist override van SetStateByFunction om te groeperen) + if Assigned(ThrottleDevice) then + begin + color := LOGI_GREEN; + + case AState of + lsOff: color := LOGI_OFF; + lsGreen: color := LOGI_GREEN; + lsAmber: color := LOGI_AMBER; + lsRed: color := LOGI_RED; + + // ToDo timers voor warning / error + lsWarning: color := LOGI_RED; + lsError: color := LOGI_RED; + end; + + SetButtonColor(ThrottleDevice, TLogiPanelButton(ALEDIndex), color); + ProcessMessages; + end; +end; + + +procedure TG940LEDStateConsumer.FindThrottleDevice; +begin + SetDeviceState(DEVICESTATE_SEARCHING); + DirectInput.EnumDevices(DI8DEVCLASS_GAMECTRL, + EnumDevicesProc, + Pointer(Self), + DIEDFL_ATTACHEDONLY); + + if not Assigned(ThrottleDevice) then + SetDeviceState(DEVICESTATE_NOTFOUND); +end; + + +procedure TG940LEDStateConsumer.FoundThrottleDevice(ADeviceGUID: TGUID); +begin + if DirectInput.CreateDevice(ADeviceGUID, FThrottleDevice, nil) = S_OK then + SetDeviceState(DEVICESTATE_FOUND); +end; + + +procedure TG940LEDStateConsumer.SetDeviceState(AState: Integer); +begin + Task.Comm.Send(MSG_NOTIFY_DEVICESTATE, AState); +end; + + +procedure TG940LEDStateConsumer.MsgFindThrottleDevice(var msg: TOmniMessage); +begin + FindThrottleDevice; +end; + + +//procedure TCustomLEDStateProvider.SetStateByFunction(AFunction: Integer; AState: TLEDState); +//var +// functionMap: TLEDFunctionMap; +// ledIndex: Integer; +// +//begin +// functionMap := LockFunctionMap; +// try +// for ledIndex := 0 to Pred(functionMap.Count) do +// if functionMap.GetFunction(ledIndex) = AFunction then +// begin +// if AState <> FState[ledIndex] then +// begin +// FState[ledIndex] := AState; +// ConsumerChannel.Send(MSG_STATECHANGED, [ledIndex, Ord(AState)]); +// end; +// end; +// finally +// UnlockFunctionMap; +// end; +//end; + +end. diff --git a/G940LEDControl/Units/LEDFunctionMap.pas b/G940LEDControl/Units/LEDFunctionMap.pas new file mode 100644 index 0000000..134f871 --- /dev/null +++ b/G940LEDControl/Units/LEDFunctionMap.pas @@ -0,0 +1,174 @@ +unit LEDFunctionMap; + +interface +uses + Classes, + SyncObjs, + + X2UtHashes; + + +type + TLEDState = (lsOff, lsGreen, lsAmber, lsRed, lsWarning, lsError); + + TLEDFunctionMap = class(TObject) + private + FFunctions: TX2IIHash; + protected + function Find(AFunction: Integer; out ALEDIndex: Integer): Boolean; + public + constructor Create; + destructor Destroy; override; + + procedure Clear; + + procedure SetFunction(ALEDIndex, AFunction: Integer); + function GetFunction(ALEDIndex: Integer): Integer; + + function HasFunction(AFunction: Integer): Boolean; + + function FindFirst(AFunction: Integer; out ALEDIndex: Integer): Boolean; + function FindNext(AFunction: Integer; out ALEDIndex: Integer): Boolean; + end; + + + TLEDStateMap = class(TObject) + private + FStates: TX2IIHash; + public + constructor Create; + destructor Destroy; override; + + procedure Clear; + + function GetState(ALEDIndex: Integer; ADefault: TLEDState = lsGreen): TLEDState; + function SetState(ALEDIndex: Integer; AState: TLEDState): Boolean; + end; + + +const + FUNCTION_NONE = 0; + + +implementation +uses + SysUtils; + + +{ TLEDFunctionMap } +constructor TLEDFunctionMap.Create; +begin + inherited; + + FFunctions := TX2IIHash.Create; +end; + + +destructor TLEDFunctionMap.Destroy; +begin + FreeAndNil(FFunctions); + + inherited; +end; + + +procedure TLEDFunctionMap.Clear; +begin + FFunctions.Clear; +end; + + +procedure TLEDFunctionMap.SetFunction(ALEDIndex, AFunction: Integer); +begin + FFunctions[ALEDIndex] := AFunction; +end; + + +function TLEDFunctionMap.GetFunction(ALEDIndex: Integer): Integer; +begin + Result := FFunctions[ALEDIndex]; +end; + + +function TLEDFunctionMap.HasFunction(AFunction: Integer): Boolean; +var + ledIndex: Integer; + +begin + Result := FindFirst(AFunction, ledIndex); +end; + + +function TLEDFunctionMap.FindFirst(AFunction: Integer; out ALEDIndex: Integer): Boolean; +begin + FFunctions.First; + Result := Find(AFunction, ALEDIndex); +end; + + +function TLEDFunctionMap.FindNext(AFunction: Integer; out ALEDIndex: Integer): Boolean; +begin + Result := Find(AFunction, ALEDIndex); +end; + + + +function TLEDFunctionMap.Find(AFunction: Integer; out ALEDIndex: Integer): Boolean; +begin + Result := False; + + while FFunctions.Next do + begin + if FFunctions.CurrentValue = AFunction then + begin + ALEDIndex := FFunctions.CurrentKey; + Result := True; + break; + end; + end; +end; + + +{ TLEDStateMap } +constructor TLEDStateMap.Create; +begin + inherited; + + FStates := TX2IIHash.Create; +end; + + +destructor TLEDStateMap.Destroy; +begin + FreeAndNil(FStates); + + inherited; +end; + + +procedure TLEDStateMap.Clear; +begin + FStates.Clear; +end; + + +function TLEDStateMap.GetState(ALEDIndex: Integer; ADefault: TLEDState): TLEDState; +begin + Result := ADefault; + if FStates.Exists(ALEDIndex) then + Result := TLEDState(FStates[ALEDIndex]); +end; + + +function TLEDStateMap.SetState(ALEDIndex: Integer; AState: TLEDState): Boolean; +begin + if FStates.Exists(ALEDIndex) then + Result := (FStates[ALEDIndex] <> Ord(AState)) + else + Result := True; + + if Result then + FStates[ALEDIndex] := Ord(AState); +end; + +end. diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index 5f428c9..7b474b8 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -2,202 +2,204 @@ unit LEDStateConsumer; interface uses - DirectInput, OtlComm, OtlTaskControl, - CustomLEDStateProvider; + LEDFunctionMap, + LEDStateProvider; + + +const + MSG_CLEAR_FUNCTIONS = 1; + MSG_SET_FUNCTION = 2; + MSG_INITIALIZE_PROVIDER = 3; + MSG_FINALIZE_PROVIDER = 4; + MSG_PROCESS_MESSAGES = 5; + + MSG_CONSUMER_OFFSET = MSG_PROCESS_MESSAGES; + + TIMER_PROCESSMESSAGES = 1; -const - MSG_FINDTHROTTLEDEVICE = 1; - MSG_NOTIFY_DEVICESTATE = 2; - - type - TG940LEDStateConsumer = class(TOmniWorker) + TLEDStateConsumer = class(TOmniWorker, ILEDStateConsumer) private - FProviderChannel: IOmniCommunicationEndpoint; - FDirectInput: IDirectInput8; - FThrottleDevice: IDirectInputDevice8; FFunctionMap: TLEDFunctionMap; + FStateMap: TLEDStateMap; + FProvider: TLEDStateProvider; + FTimerSet: Boolean; protected - procedure MsgFindThrottleDevice(var msg: TOmniMessage); message MSG_FINDTHROTTLEDEVICE; - procedure MsgUpdateFunctionMap(var msg: TOmniMessage); message MSG_UPDATEFUNCTIONMAP; - procedure MsgSetStateByFunction(var msg: TOmniMessage); message MSG_SETSTATEBYFUNCTION; - protected - function Initialize: Boolean; override; + procedure MsgClearFunctions(var msg: TOmniMessage); message MSG_CLEAR_FUNCTIONS; + procedure MsgSetFunction(var msg: TOmniMessage); message MSG_SET_FUNCTION; + procedure MsgInitializeProvider(var msg: TOmniMessage); message MSG_INITIALIZE_PROVIDER; + procedure MsgFinalizeProvider(var msg: TOmniMessage); message MSG_FINALIZE_PROVIDER; + procedure MsgProcessMessages(var msg: TOmniMessage); message MSG_PROCESS_MESSAGES; - procedure FindThrottleDevice; - procedure FoundThrottleDevice(ADeviceGUID: TGUID); + procedure InitializeProvider(AProviderClass: TLEDStateProviderClass); + procedure FinalizeProvider; - procedure SetDeviceState(AState: Integer); + procedure LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); virtual; - property DirectInput: IDirectInput8 read FDirectInput; - property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice; + { ILEDStateConsumer } + function GetFunctionMap: TLEDFunctionMap; + procedure SetStateByFunction(AFunction: Integer; AState: TLEDState); + + property FunctionMap: TLEDFunctionMap read GetFunctionMap; + property StateMap: TLEDStateMap read FStateMap; + property Provider: TLEDStateProvider read FProvider; public - constructor Create(AProviderChannel: IOmniCommunicationEndpoint); + constructor Create; destructor Destroy; override; + + class procedure ClearFunctions(AConsumer: IOmniTaskControl); + class procedure SetFunction(AConsumer: IOmniTaskControl; ALEDIndex, AFunction: Integer); + class procedure InitializeStateProvider(AConsumer: IOmniTaskControl; AProviderClass: TLEDStateProviderClass); + class procedure FinalizeStateProvider(AConsumer: IOmniTaskControl); end; -const - EXIT_ERROR_LOGIJOYSTICKDLL = 1; - EXIT_ERROR_DIRECTINPUT = 2; - - - DEVICESTATE_SEARCHING = 0; - DEVICESTATE_FOUND = 1; - DEVICESTATE_NOTFOUND = 2; - implementation uses SysUtils, - Windows, - LogiJoystickDLL; + OtlCommon; -function EnumDevicesProc(var lpddi: TDIDeviceInstanceA; pvRef: Pointer): BOOL; stdcall; -var - vendorID: Word; - productID: Word; - +{ TLEDStateConsumer } +constructor TLEDStateConsumer.Create; begin - Result := True; + inherited; - vendorID := LOWORD(lpddi.guidProduct.D1); - productID := HIWORD(lpddi.guidProduct.D1); - - if (vendorID = VENDOR_LOGITECH) and - (productID = PRODUCT_G940_THROTTLE) then - begin - TG940LEDStateConsumer(pvRef).FoundThrottleDevice(lpddi.guidInstance); - Result := False; - end; -end; - - - -{ TG940LEDStateConsumer } -constructor TG940LEDStateConsumer.Create(AProviderChannel: IOmniCommunicationEndpoint); -begin - inherited Create; - - FProviderChannel := AProviderChannel; FFunctionMap := TLEDFunctionMap.Create; + FStateMap := TLEDStateMap.Create; end; -destructor TG940LEDStateConsumer.Destroy; +destructor TLEDStateConsumer.Destroy; begin + FinalizeProvider; + + FreeAndNil(FStateMap); FreeAndNil(FFunctionMap); inherited; end; -function TG940LEDStateConsumer.Initialize: Boolean; +function TLEDStateConsumer.GetFunctionMap: TLEDFunctionMap; begin - Result := False; - - if not LogiJoystickDLLInitialized then - begin - Task.SetExitStatus(EXIT_ERROR_LOGIJOYSTICKDLL, 'Could not load LogiJoystickDLL.dll'); - exit; - end; - -// btnRetry.Visible := False; -// SetState(STATE_SEARCHING, False); - - if DirectInput8Create(SysInit.HInstance, DIRECTINPUT_VERSION, IDirectInput8, FDirectInput, nil) <> S_OK then - begin - Task.SetExitStatus(EXIT_ERROR_DIRECTINPUT, 'Failed to initialize DirectInput'); - exit; - end; - - Result := True; - - Task.RegisterComm(FProviderChannel); - FindThrottleDevice; + Result := FFunctionMap; end; -procedure TG940LEDStateConsumer.FindThrottleDevice; -begin - SetDeviceState(DEVICESTATE_SEARCHING); - DirectInput.EnumDevices(DI8DEVCLASS_GAMECTRL, - EnumDevicesProc, - Pointer(Self), - DIEDFL_ATTACHEDONLY); - - if not Assigned(ThrottleDevice) then - SetDeviceState(DEVICESTATE_NOTFOUND); -end; - - -procedure TG940LEDStateConsumer.FoundThrottleDevice(ADeviceGUID: TGUID); -begin - if DirectInput.CreateDevice(ADeviceGUID, FThrottleDevice, nil) = S_OK then - SetDeviceState(DEVICESTATE_FOUND); -end; - - -procedure TG940LEDStateConsumer.SetDeviceState(AState: Integer); -begin - Task.Comm.Send(MSG_NOTIFY_DEVICESTATE, AState); -end; - - -procedure TG940LEDStateConsumer.MsgFindThrottleDevice(var msg: TOmniMessage); -begin - FindThrottleDevice; -end; - - -procedure TG940LEDStateConsumer.MsgUpdateFunctionMap(var msg: TOmniMessage); +procedure TLEDStateConsumer.SetStateByFunction(AFunction: Integer; AState: TLEDState); var - provider: TCustomLEDStateProvider; - functionMap: TLEDFunctionMap; + ledIndex: Integer; begin - provider := (msg.MsgData.AsObject as TCustomLEDStateProvider); - functionMap := provider.LockFunctionMap; - try - FFunctionMap.Assign(functionMap); - finally - provider.UnlockFunctionMap; + if FunctionMap.FindFirst(AFunction, ledIndex) then + repeat + if StateMap.SetState(ledIndex, AState) then + LEDStateChanged(ledIndex, AState); + until not FunctionMap.FindNext(AFunction, ledIndex); +end; + + + +procedure TLEDStateConsumer.MsgClearFunctions(var msg: TOmniMessage); +begin + FunctionMap.Clear; +end; + + +procedure TLEDStateConsumer.MsgSetFunction(var msg: TOmniMessage); +var + values: TOmniValueContainer; + +begin + values := msg.MsgData.AsArray; + FunctionMap.SetFunction(values[0], values[1]); +end; + + +procedure TLEDStateConsumer.MsgInitializeProvider(var msg: TOmniMessage); +begin + InitializeProvider(TLEDStateProviderClass(msg.MsgData.AsPointer)); +end; + + +procedure TLEDStateConsumer.MsgFinalizeProvider(var msg: TOmniMessage); +begin + FinalizeProvider; +end; + + +procedure TLEDStateConsumer.MsgProcessMessages(var msg: TOmniMessage); +begin + Provider.ProcessMessages; +end; + + +procedure TLEDStateConsumer.InitializeProvider(AProviderClass: TLEDStateProviderClass); +begin + FinalizeProvider; + + FProvider := AProviderClass.Create(Self); + // ToDo exception handlign + Provider.Initialize; + + if Provider.ProcessMessagesInterval > -1 then + begin + Task.SetTimer(TIMER_PROCESSMESSAGES, Provider.ProcessMessagesInterval, MSG_PROCESS_MESSAGES); + FTimerSet := True; end; end; -procedure TG940LEDStateConsumer.MsgSetStateByFunction(var msg: TOmniMessage); +procedure TLEDStateConsumer.FinalizeProvider; begin - // + if Assigned(Provider) then + begin + if FTimerSet then + begin + Task.ClearTimer(TIMER_PROCESSMESSAGES); + FTimerSet := False; + end; + + Provider.Terminate; + Provider.Finalize; + FreeAndNil(FProvider); + end; end; -//procedure TCustomLEDStateProvider.SetStateByFunction(AFunction: Integer; AState: TLEDState); -//var -// functionMap: TLEDFunctionMap; -// ledIndex: Integer; -// -//begin -// functionMap := LockFunctionMap; -// try -// for ledIndex := 0 to Pred(functionMap.Count) do -// if functionMap.GetFunction(ledIndex) = AFunction then -// begin -// if AState <> FState[ledIndex] then -// begin -// FState[ledIndex] := AState; -// ConsumerChannel.Send(MSG_STATECHANGED, [ledIndex, Ord(AState)]); -// end; -// end; -// finally -// UnlockFunctionMap; -// end; -//end; +procedure TLEDStateConsumer.LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); +begin +end; + + +class procedure TLEDStateConsumer.ClearFunctions(AConsumer: IOmniTaskControl); +begin + AConsumer.Comm.Send(MSG_CLEAR_FUNCTIONS); +end; + + +class procedure TLEDStateConsumer.SetFunction(AConsumer: IOmniTaskControl; ALEDIndex, AFunction: Integer); +begin + AConsumer.Comm.Send(MSG_SET_FUNCTION, [ALEDIndex, AFunction]); +end; + + +class procedure TLEDStateConsumer.InitializeStateProvider(AConsumer: IOmniTaskControl; AProviderClass: TLEDStateProviderClass); +begin + AConsumer.Comm.Send(MSG_INITIALIZE_PROVIDER, Pointer(AProviderClass)); +end; + + +class procedure TLEDStateConsumer.FinalizeStateProvider(AConsumer: IOmniTaskControl); +begin + AConsumer.Comm.Send(MSG_FINALIZE_PROVIDER); +end; end. diff --git a/G940LEDControl/Units/LEDStateProvider.pas b/G940LEDControl/Units/LEDStateProvider.pas new file mode 100644 index 0000000..b7df4c6 --- /dev/null +++ b/G940LEDControl/Units/LEDStateProvider.pas @@ -0,0 +1,111 @@ +unit LEDStateProvider; + +interface +uses + Classes, + SyncObjs, + SysUtils, + + LEDFunctionMap; + +type + EInitializeError = class(Exception) + private + FExitCode: Integer; + public + constructor Create(const Msg: string; ExitCode: Integer = 0); + + property ExitCode: Integer read FExitCode write FExitCode; + end; + + + ILEDStateConsumer = interface + ['{6E630C92-7C5C-4D16-8BED-AE27559FA584}'] + function GetFunctionMap: TLEDFunctionMap; + procedure SetStateByFunction(AFunction: Integer; AState: TLEDState); + + property FunctionMap: TLEDFunctionMap read GetFunctionMap; + end; + + + TLEDStateProvider = class(TObject) + private + FConsumer: ILEDStateConsumer; + FTerminated: Boolean; + protected + procedure Execute; virtual; abstract; + + function GetProcessMessagesInterval: Integer; virtual; + + property Consumer: ILEDStateConsumer read FConsumer; + public + constructor Create(AConsumer: ILEDStateConsumer); + destructor Destroy; override; + + procedure Initialize; virtual; + procedure Finalize; virtual; + procedure ProcessMessages; virtual; + + procedure Terminate; virtual; + + property ProcessMessagesInterval: Integer read GetProcessMessagesInterval; + property Terminated: Boolean read FTerminated; + end; + + TLEDStateProviderClass = class of TLEDStateProvider; + + +implementation + + +{ TCustomLEDStateProvider } +constructor TLEDStateProvider.Create(AConsumer: ILEDStateConsumer); +begin + inherited Create; + + FConsumer := AConsumer; +end; + + +destructor TLEDStateProvider.Destroy; +begin + inherited; +end; + + +procedure TLEDStateProvider.Initialize; +begin +end; + + +procedure TLEDStateProvider.Finalize; +begin +end; + + +procedure TLEDStateProvider.ProcessMessages; +begin +end; + + +procedure TLEDStateProvider.Terminate; +begin + FTerminated := True; +end; + + +function TLEDStateProvider.GetProcessMessagesInterval: Integer; +begin + Result := -1; +end; + + +{ EInitializeError } +constructor EInitializeError.Create(const Msg: string; ExitCode: Integer); +begin + inherited Create(Msg); + + FExitCode := ExitCode; +end; + +end.