You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

336 lines
14 KiB

  1. unit uutlEvent;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, syncobjs,
  6. uutlTypes, uutlCommon, uutlGenerics, uutlInterfaces;
  7. type
  8. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  9. IutlEventArgs = interface(IUnknown)
  10. ['{FC7AA96D-9C2C-42AD-A680-DE55341F2B35}']
  11. end;
  12. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  13. IutlEventListener = interface(IUnknown)
  14. ['{BC45E26B-96F7-4151-87F1-C330C8C668E5}']
  15. procedure DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  16. end;
  17. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  18. TutlEventHandler = procedure (aSender: TObject; aEventArgs: IutlEventArgs) of object;
  19. TutlEventArgs = class(TutlInterfacedObject, IutlEventArgs)
  20. public
  21. constructor Create;
  22. end;
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  24. IutlObservable = interface(IUnknown)
  25. ['{C54BD844-8273-4ACF-90C5-05DACF4359AF}']
  26. procedure RegisterEventListener (aListener: IutlEventListener);
  27. procedure UnregisterEventListener(aListener: IutlEventListener);
  28. end;
  29. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  30. generic TutlEventList<T> = class(specialize TutlCustomHashSet<T>)
  31. private type
  32. TComparer = class(TInterfacedObject, IComparer)
  33. public
  34. function EqualityCompare(constref i1, i2: T): Boolean;
  35. function Compare (constref i1, i2: T): Integer;
  36. end;
  37. public
  38. constructor Create;
  39. end;
  40. TutlNotifyEventList = specialize TutlEventList<TNotifyEvent>;
  41. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  42. TutlEventListenerList = class(
  43. specialize TutlEventList<TutlEventHandler>
  44. , IutlEventListener)
  45. public { IutlEventListener }
  46. procedure DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  47. public
  48. constructor Create;
  49. end;
  50. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  51. TutlEventListenerSet = class(
  52. specialize TutlCustomHashSet<IutlEventListener>
  53. , IutlEventListener
  54. , IutlObservable)
  55. private type
  56. TComparer = class(TInterfacedObject, IComparer)
  57. public
  58. function EqualityCompare(constref i1, i2: IutlEventListener): Boolean;
  59. function Compare (constref i1, i2: IutlEventListener): Integer;
  60. end;
  61. public { IutlEventListener }
  62. procedure DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  63. public { IutlObservable }
  64. procedure RegisterEventListener (aListener: IutlEventListener);
  65. procedure UnregisterEventListener(aListener: IutlEventListener);
  66. public
  67. constructor Create; reintroduce;
  68. end;
  69. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  70. TutlEventListenerCallback = class(
  71. TInterfacedObject
  72. , IutlEventListener)
  73. private
  74. fHandler: TutlEventHandler;
  75. public { IEventListener }
  76. procedure DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  77. public
  78. constructor Create(const aHandler: TutlEventHandler);
  79. end;
  80. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  81. TutlEventListenerAsync = class(
  82. TInterfacedObject
  83. , IutlEventListener)
  84. private type
  85. TEventPair = specialize TutlPair<TObject, IutlEventArgs>;
  86. TEventQueue = class(specialize TutlQueue<TEventPair>)
  87. protected
  88. procedure Release(var aItem: TEventPair; const aFreeItem: Boolean); override;
  89. end;
  90. private
  91. fEventLock: TCriticalSection;
  92. fEvents: TEventQueue;
  93. fListener: IutlEventListener;
  94. function PopEventPair(out aPair: TEventPair): Boolean;
  95. public { IEventListener }
  96. procedure DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  97. public
  98. procedure DispatchEvents;
  99. constructor Create(const aListener: IutlEventListener);
  100. destructor Destroy; override;
  101. end;
  102. implementation
  103. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  104. //TutlEventArgs/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  105. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  106. constructor TutlEventArgs.Create;
  107. begin
  108. inherited Create;
  109. AutoFree := true;
  110. end;
  111. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112. //TutlEventList/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  113. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  114. function TutlEventList.TComparer.EqualityCompare(constref i1, i2: T): Boolean;
  115. begin
  116. result := (TMethod(i1).Data = TMethod(i2).Data)
  117. and (TMethod(i1).Code = TMethod(i2).Code);
  118. end;
  119. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  120. function TutlEventList.TComparer.Compare(constref i1, i2: T): Integer;
  121. var
  122. m1, m2: TMethod;
  123. begin
  124. m1 := TMethod(i1);
  125. m2 := TMethod(i2);
  126. if (m1.Data < m2.Data) then
  127. result := -1
  128. else if (m1.Data > m2.Data) then
  129. result := 1
  130. else if (m1.Code < m2.Code) then
  131. result := -1
  132. else if (m1.Code > m2.Code) then
  133. result := 1
  134. else
  135. result := 0;
  136. end;
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  138. constructor TutlEventList.Create;
  139. begin
  140. inherited Create(TComparer.Create, true);
  141. end;
  142. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  143. //TutlEventListenerList//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  144. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  145. procedure TutlEventListenerList.DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  146. var
  147. e: TutlEventHandler;
  148. begin
  149. for e in self do
  150. e(aSender, aEventArgs);
  151. end;
  152. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  153. constructor TutlEventListenerList.Create;
  154. begin
  155. inherited Create;
  156. AutoFree := true;
  157. end;
  158. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  159. //TutlEventListenerSet///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  160. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  161. function TutlEventListenerSet.TComparer.EqualityCompare(constref i1, i2: IutlEventListener): Boolean;
  162. begin
  163. result := (i1 = i2);
  164. end;
  165. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  166. function TutlEventListenerSet.TComparer.Compare(constref i1, i2: IutlEventListener): Integer;
  167. begin
  168. if (Pointer(i1) < Pointer(i2)) then
  169. result := -1
  170. else if (Pointer(i1) > Pointer(i2)) then
  171. result := 1
  172. else
  173. result := 0;
  174. end;
  175. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  176. //TutlEventListenerSet///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  177. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  178. procedure TutlEventListenerSet.DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  179. var
  180. e: IutlEventListener;
  181. begin
  182. for e in self do
  183. e.DispatchEvent(aSender, aEventArgs);
  184. end;
  185. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  186. procedure TutlEventListenerSet.RegisterEventListener(aListener: IutlEventListener);
  187. begin
  188. Add(aListener);
  189. end;
  190. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  191. procedure TutlEventListenerSet.UnregisterEventListener(aListener: IutlEventListener);
  192. begin
  193. Remove(aListener);
  194. end;
  195. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  196. constructor TutlEventListenerSet.Create;
  197. begin
  198. inherited Create(TComparer.Create, true);
  199. end;
  200. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  201. //TutlEventListenerCallback//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  202. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  203. procedure TutlEventListenerCallback.DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  204. begin
  205. fHandler(aSender, aEventArgs);
  206. end;
  207. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  208. constructor TutlEventListenerCallback.Create(const aHandler: TutlEventHandler);
  209. begin
  210. inherited Create;
  211. if not Assigned(aHandler) then
  212. raise EArgumentNilException.Create('aHandler is not assigned');
  213. fHandler := aHandler;
  214. end;
  215. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  216. //TutlEventListenerAsync.TEventList//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  217. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  218. procedure TutlEventListenerAsync.TEventQueue.Release(var aItem: TEventPair; const aFreeItem: Boolean);
  219. begin
  220. aItem.first := nil;
  221. aItem.second := nil;
  222. inherited Release(aItem, aFreeItem);
  223. end;
  224. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  225. //TutlEventListenerAsync/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  226. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  227. function TutlEventListenerAsync.PopEventPair(out aPair: TEventPair): Boolean;
  228. begin
  229. fEventLock.Enter;
  230. try
  231. result := not fEvents.IsEmpty;
  232. if result
  233. then aPair := fEvents.Dequeue
  234. else FillByte(aPair, SizeOf(aPair), 0);
  235. finally
  236. fEventLock.Leave;
  237. end;
  238. end;
  239. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  240. procedure TutlEventListenerAsync.DispatchEvent(aSender: TObject; aEventArgs: IutlEventArgs);
  241. var
  242. p: TEventPair;
  243. begin
  244. p.first := aSender;
  245. p.second := aEventArgs;
  246. fEventLock.Enter;
  247. try
  248. fEvents.Enqueue(p);
  249. finally
  250. fEventLock.Leave;
  251. end;
  252. end;
  253. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  254. procedure TutlEventListenerAsync.DispatchEvents;
  255. var
  256. p: TEventPair;
  257. begin
  258. while PopEventPair(p) do
  259. fListener.DispatchEvent(p.first, p.second);
  260. end;
  261. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  262. constructor TutlEventListenerAsync.Create(const aListener: IutlEventListener);
  263. begin
  264. if not Assigned(aListener) then
  265. raise EArgumentNilException.Create('aListener');
  266. inherited Create;
  267. fEventLock := TCriticalSection.Create;
  268. fEvents := TEventQueue.Create(true);
  269. fListener := aListener;
  270. end;
  271. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  272. destructor TutlEventListenerAsync.Destroy;
  273. begin
  274. fEventLock.Enter;
  275. try
  276. FreeAndNil(fEvents);
  277. fListener := nil;
  278. finally
  279. fEventLock.Leave;
  280. end;
  281. FreeAndNil(fEventLock);
  282. inherited Destroy;
  283. end;
  284. end.