unit uutlEvent; {$mode objfpc}{$H+} interface uses Classes, SysUtils, syncobjs, uutlTypes, uutlCommon, uutlGenerics, uutlInterfaces; type ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// IutlEventArgs = interface(IUnknown) ['{FC7AA96D-9C2C-42AD-A680-DE55341F2B35}'] end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// IutlEventListener = interface(IUnknown) ['{BC45E26B-96F7-4151-87F1-C330C8C668E5}'] procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventHandler = procedure (constref aSender: TObject; constref aEvent: IutlEventArgs) of object; TutlEventArgs = class(TInterfacedObject, IutlEventArgs); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// generic IutlObservable = interface(specialize {$IFDEF UTL_ADVANCED_ENUMERATORS}IutlEnumerable{$ELSE}IEnumerable{$ENDIF}) ['{C54BD844-8273-4ACF-90C5-05DACF4359AF}'] procedure RegisterEventHandler (const aHandler: TutlEventHandler); procedure UnregisterEventHandler(const aHandler: TutlEventHandler); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// generic TutlEventList = class(specialize TutlCustomHashSet) private type TComparer = class(TInterfacedObject, IComparer) public function EqualityCompare(constref i1, i2: T): Boolean; function Compare (constref i1, i2: T): Integer; end; public constructor Create; end; TutlNotifyEventList = specialize TutlEventList; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventListenerSet = class( specialize TutlCustomHashSet , IutlEventListener) private type TComparer = class(TInterfacedObject, IComparer) public function EqualityCompare(constref i1, i2: IutlEventListener): Boolean; function Compare (constref i1, i2: IutlEventListener): Integer; end; public { IutlEventListener } procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs); public constructor Create; reintroduce; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventListenerCallback = class( TInterfacedObject , IutlEventListener) private fHandler: TutlEventHandler; public { IEventListener } procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs); public constructor Create(const aHandler: TutlEventHandler); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TutlEventListenerAsync = class( TutlInterfaceNoRefCount , IutlEventListener) private type TEventPair = specialize TutlPair; TEventList = class(specialize TutlSimpleList) protected procedure Release(var aItem: TEventPair; const aFreeItem: Boolean); override; end; private fEventLock: TCriticalSection; fListenerLock: TCriticalSection; fEvents: TEventList; fListener: TutlEventListenerSet; function PopEventPair(out aPair: TEventPair): Boolean; public { IEventListener } procedure DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs); public function RegisterListener (const aListener: IutlEventListener): Boolean; function UnregisterListener(const aListener: IutlEventListener): Boolean; procedure DispatchEvents; constructor Create; destructor Destroy; override; end; implementation //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventList///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventList.TComparer.EqualityCompare(constref i1, i2: T): Boolean; begin result := (TMethod(i1).Data = TMethod(i2).Data) and (TMethod(i1).Code = TMethod(i2).Code); end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventList.TComparer.Compare(constref i1, i2: T): Integer; var m1, m2: TMethod; begin m1 := TMethod(i1); m2 := TMethod(i2); if (m1.Data < m2.Data) then result := -1 else if (m1.Data > m2.Data) then result := 1 else if (m1.Code < m2.Code) then result := -1 else if (m1.Code > m2.Code) then result := 1 else result := 0; end; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventList.Create; begin inherited Create(TComparer.Create, true); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerSet/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerSet.TComparer.EqualityCompare(constref i1, i2: IutlEventListener): Boolean; begin result := (i1 = i2); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerSet.TComparer.Compare(constref 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/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerSet.DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs); var l: IutlEventListener; begin for l in self do l.DispatchEvent(aSender, aEvent); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventListenerSet.Create; begin inherited Create(TComparer.Create, true); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerCallback////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerCallback.DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs); begin fHandler(aSender, aEvent); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventListenerCallback.Create(const aHandler: TutlEventHandler); begin inherited Create; if not Assigned(aHandler) then raise EArgumentNilException.Create('aHandler is not assigned'); fHandler := aHandler; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerAsync.TEventList////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerAsync.TEventList.Release(var aItem: TEventPair; const aFreeItem: Boolean); begin aItem.first := nil; aItem.second := nil; inherited Release(aItem, aFreeItem); end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //TutlEventListenerAsync///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerAsync.PopEventPair(out aPair: TEventPair): Boolean; begin fEventLock.Enter; try result := not fEvents.IsEmpty; if result then aPair := fEvents.PopFirst(false) else FillByte(aPair, SizeOf(aPair), 0); finally fEventLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerAsync.DispatchEvent(constref aSender: TObject; constref aEvent: IutlEventArgs); var p: TEventPair; begin p.first := aSender; p.second := aEvent; fEventLock.Enter; try fEvents.Add(p); finally fEventLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerAsync.RegisterListener(const aListener: IutlEventListener): Boolean; begin fListenerLock.Enter; try result := fListener.Add(aListener); finally fListenerLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TutlEventListenerAsync.UnregisterListener(const aListener: IutlEventListener): Boolean; begin fListenerLock.Enter; try result := fListener.Remove(aListener); finally fListenerLock.Leave; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TutlEventListenerAsync.DispatchEvents; var p: TEventPair; begin while PopEventPair(p) do begin fListenerLock.Enter; try fListener.DispatchEvent(p.first, p.second); finally fListenerLock.Leave; end; end; end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TutlEventListenerAsync.Create; begin inherited Create; fEventLock := TCriticalSection.Create; fListenerLock := TCriticalSection.Create; fEvents := TEventList.Create(true); 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.