unit uutlEvent; {$mode objfpc}{$H+} interface uses Classes, SysUtils, syncobjs, uutlCommon, uutlGenerics; type ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventType = byte; TutlEventTypes = set of TutlEventType; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// IutlEvent = interface(IUnknown) ['{FC7AA96D-9C2C-42AD-A680-DE55341F2B35}'] end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventList = class(specialize TutlSimpleList) public constructor Create; reintroduce; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEvent = class(TInterfacedObject, IutlEvent) private fSender: TObject; fEventType: TutlEventType; fTimestamp: Single; public property Sender: TObject read fSender; property EventType: TutlEventType read fEventType; property Timestamp: Single read fTimestamp; constructor Create(const aSender: TObject; const aEventType: TutlEventType); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// IutlEventListener = interface(IUnknown) ['{BC45E26B-96F7-4151-87F1-C330C8C668E5}'] procedure DispatchEvent(aEvent: IutlEvent); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventListenerSet = class(specialize TutlHashSetBase) private type TComparer = class(TInterfacedObject, IComparer) function Compare(const i1, i2: IutlEventListener): Integer; end; function GetEmpty: Boolean; public property Empty: Boolean read GetEmpty; procedure DispatchEvent(aEvent: IutlEvent); virtual; function RegisterListener(const aListener: IutlEventListener): Boolean; function UnregisterListener(const aListener: IutlEventListener): Boolean; constructor Create; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventListenerCallback = class(TInterfacedObject, IutlEventListener) public type TCallback = procedure(aEvent: IutlEvent) of object; private fCallback: TCallback; private { IEventListener } procedure DispatchEvent(aEvent: IutlEvent); public constructor Create(const aCallback: TCallback); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventListenerAsync = class(TutlInterfaceNoRefCount, IutlEventListener) private fEventLock: TCriticalSection; fListenerLock: TCriticalSection; fEvents: TutlEventList; fListener: TutlEventListenerSet; function PopEvent: IutlEvent; private { IEventListener } procedure DispatchEvent(aEvent: IutlEvent); public function RegisterListener(const aListener: IutlEventListener): Boolean; function UnregisterListener(const aListener: IutlEventListener): Boolean; procedure DispatchEvents; constructor Create; destructor Destroy; override; end; implementation uses {$IFDEF LOG_DEBUG} uutlLogger, {$ENDIF} uutlTiming; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventList////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventList.Create; begin inherited Create(true); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEvent////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEvent.Create(const aSender: TObject; const aEventType: TutlEventType); begin inherited Create; fSender := aSender; fEventType := aEventType; fTimestamp := GetMicroTime / 1000000; {$IFDEF LOG_DEBUG} utlLogger.Debug(self, 'dispatch event (Sender=%s[%p]; EventType=%0.10d; Timestamp=%10.5f)', [ fSender.ClassName, Pointer(fSender), fEventType, fTimestamp ]); {$ENDIF} end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerSet.TComparer///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerSet.TComparer.Compare(const i1, i2: IutlEventListener): Integer; begin if (Pointer(i1) < Pointer(i2)) then result := -1 else if (Pointer(i1) > Pointer(i2)) then result := 1 else result := 0; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerSet/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerSet.GetEmpty: Boolean; begin result := (GetCount = 0); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerSet.DispatchEvent(aEvent: IutlEvent); var l: IutlEventListener; begin for l in self do l.DispatchEvent(aEvent); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerSet.RegisterListener(const aListener: IutlEventListener): Boolean; var i: Integer; begin result := (SearchItem(0, List.Count-1, aListener, i) < 0); if result then InsertIntern(i, aListener); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerSet.UnregisterListener(const aListener: IutlEventListener): Boolean; var i, tmp: Integer; begin i := SearchItem(0, List.Count-1, aListener, tmp); result := (i >= 0); if result then DeleteIntern(i); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventListenerSet.Create; begin inherited Create(TComparer.Create, true); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerCallback////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerCallback.DispatchEvent(aEvent: IutlEvent); begin fCallback(aEvent); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventListenerCallback.Create(const aCallback: TCallback); begin inherited Create; if not Assigned(aCallback) then raise EArgumentException.Create('aCallback is not assigned'); fCallback := aCallback; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerAsync///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerAsync.PopEvent: IutlEvent; begin fEventLock.Enter; try if (fEvents.Count > 0) then result := fEvents.PopFirst(false) else result := nil; finally fEventLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerAsync.DispatchEvent(aEvent: IutlEvent); begin fEventLock.Enter; try fEvents.Add(aEvent); finally fEventLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerAsync.RegisterListener(const aListener: IutlEventListener): Boolean; begin fListenerLock.Enter; try result := fListener.RegisterListener(aListener); finally fListenerLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerAsync.UnregisterListener(const aListener: IutlEventListener): Boolean; begin fListenerLock.Enter; try result := fListener.UnregisterListener(aListener); finally fListenerLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerAsync.DispatchEvents; var e: IutlEvent; begin repeat e := PopEvent; if Assigned(e) then begin fListenerLock.Enter; try fListener.DispatchEvent(e); finally fListenerLock.Leave; end; end; until not Assigned(e); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventListenerAsync.Create; begin inherited Create; fEventLock := TCriticalSection.Create; fListenerLock := TCriticalSection.Create; fEvents := TutlEventList.Create; fListener := TutlEventListenerSet.Create; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// destructor TutlEventListenerAsync.Destroy; begin fEventLock.Enter; fListenerLock.Enter; try FreeAndNil(fEvents); FreeAndNil(fListener); finally fListenerLock.Leave; fEventLock.Leave; end; FreeAndNil(fEventLock); FreeAndNil(fListenerLock); inherited Destroy; end; end.