GXS.Cadencer.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.Cadencer;
  5. (* Cadencing composant for ease Progress processing *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.Windows,
  10. System.Messaging,
  11. Winapi.Messages,
  12. System.Classes,
  13. System.Types,
  14. System.SysUtils,
  15. FMX.Forms,
  16. GXS.Scene,
  17. GXS.BaseClasses;
  18. type
  19. (* Determines how the TgxCadencer operates.
  20. - cmManual : you must trigger progress manually (in your code)
  21. - cmASAP : progress is triggered As Soon As Possible after a previous
  22. progress (uses windows messages).
  23. - cmApplicationIdle : will hook Application.OnIdle, this will overwrite
  24. any previous event handle, and only one cadencer may be in this mode. *)
  25. TgxCadencerMode = (cmManual, cmASAP, cmApplicationIdle);
  26. (* Determines which time reference the TgxCadencer should use.
  27. - cmRTC : the Real Time Clock is used (precise over long periods, but
  28. not accurate to the millisecond, may limit your effective framerate
  29. to less than 50 FPS on some systems)
  30. - cmPerformanceCounter : the windows performance counter is used (nice
  31. precision, may derive over long periods, this is the default option
  32. as it allows the smoothest animation on fast systems)
  33. - cmExternal : the CurrentTime property is used *)
  34. TgxCadencerTimeReference = (cmRTC, cmPerformanceCounter, cmExternal);
  35. (* This component allows auto-progression of animation.
  36. Basicly dropping this component and linking it to your TgxScene will send
  37. it real-time progression events (time will be measured in seconds) while
  38. keeping the CPU 100% busy if possible (ie. if things change in your scene).
  39. The progression time (the one you'll see in you progression events)
  40. is calculated using (CurrentTime-OriginTime)*TimeMultiplier,
  41. CurrentTime being either manually or automatically updated using
  42. TimeReference (setting CurrentTime does NOT trigger progression). *)
  43. TgxCadencer = class(TComponent)
  44. private
  45. FSubscribedCadenceableComponents: TList;
  46. FScene: TgxScene;
  47. FTimeMultiplier: Double;
  48. lastTime, downTime, lastMultiplier: Double;
  49. FEnabled: Boolean;
  50. FSleepLength: Integer;
  51. FMode: TgxCadencerMode;
  52. FTimeReference: TgxCadencerTimeReference;
  53. FCurrentTime: Double;
  54. FOriginTime: Double;
  55. FMaxDeltaTime, FMinDeltaTime, FFixedDeltaTime: Double;
  56. FOnProgress, FOnTotalProgress : TgxProgressEvent;
  57. FProgressing: Integer;
  58. procedure SetCurrentTime(const Value: Double);
  59. protected
  60. procedure Notification(AComponent: TComponent; Operation: TOperation);
  61. override;
  62. function StoreTimeMultiplier: Boolean;
  63. procedure SetEnabled(const val: Boolean);
  64. procedure SetScene(const val: TgxScene);
  65. procedure SetMode(const val: TgxCadencerMode);
  66. procedure SetTimeReference(const val: TgxCadencerTimeReference);
  67. procedure SetTimeMultiplier(const val: Double);
  68. // Returns raw ref time (no multiplier, no offset)
  69. function GetRawReferenceTime: Double;
  70. procedure RestartASAP;
  71. procedure Loaded; override;
  72. procedure OnIdleEvent(Sender: TObject; var Done: Boolean);
  73. public
  74. constructor Create(AOwner: TComponent); override;
  75. destructor Destroy; override;
  76. procedure Subscribe(aComponent: TgxCadenceAbleComponent);
  77. procedure UnSubscribe(aComponent: TgxCadenceAbleComponent);
  78. (* Allows to manually trigger a progression.
  79. Time stuff is handled automatically.
  80. If cadencer is disabled, this functions does nothing. *)
  81. procedure Progress;
  82. // Adjusts CurrentTime if necessary, then returns its value.
  83. function GetCurrenttime: Double;
  84. (* Returns True if a "Progress" is underway.
  85. Be aware that as long as IsBusy is True, the Cadencer may be
  86. sending messages and progression calls to cadenceable components
  87. and scenes. *)
  88. function IsBusy: Boolean;
  89. // Reset the time parameters and returns to zero.
  90. procedure Reset;
  91. // Value soustracted to current time to obtain progression time.
  92. property OriginTime: Double read FOriginTime write FOriginTime;
  93. // Current time (manually or automatically set, see TimeReference).
  94. property CurrentTime: Double read FCurrentTime write SetCurrentTime;
  95. published
  96. // The TgxScene that will be cadenced (progressed).
  97. property Scene: TgxScene read FScene write SetScene;
  98. (* Enables/Disables cadencing.
  99. Disabling won't cause a jump when restarting, it is working like
  100. a play/pause (ie. may modify OriginTime to keep things smooth). *)
  101. property Enabled: Boolean read FEnabled write SetEnabled default True;
  102. (* Defines how CurrentTime is updated.
  103. See TgxCadencerTimeReference.
  104. Dynamically changeing the TimeReference may cause a "jump". *)
  105. property TimeReference: TgxCadencerTimeReference read FTimeReference write
  106. SetTimeReference default cmPerformanceCounter;
  107. (* Multiplier applied to the time reference.
  108. Zero isn't an allowed value, and be aware that if negative values
  109. are accepted, they may not be supported by other GLScene objects.
  110. Changing the TimeMultiplier will alter OriginTime. *)
  111. property TimeMultiplier: Double read FTimeMultiplier write SetTimeMultiplier
  112. stored StoreTimeMultiplier;
  113. (* Maximum value for deltaTime in progression events.
  114. If null or negative, no max deltaTime is defined, otherwise, whenever
  115. an event whose actual deltaTime would be superior to MaxDeltaTime
  116. occurs, deltaTime is clamped to this max, and the extra time is hidden
  117. by the cadencer (it isn't visible in CurrentTime either).
  118. This option allows to limit progression rate in simulations where
  119. high values would result in errors/random behaviour. *)
  120. property MaxDeltaTime: Double read FMaxDeltaTime write FMaxDeltaTime;
  121. (* Minimum value for deltaTime in progression events.
  122. If superior to zero, this value specifies the minimum time step
  123. between two progression events.
  124. This option allows to limit progression rate in simulations where
  125. low values would result in errors/random behaviour. *)
  126. property MinDeltaTime: Double read FMinDeltaTime write FMinDeltaTime;
  127. (* Fixed time-step value for progression events.
  128. If superior to zero, progression steps will happen with that fixed
  129. delta time. The progression remains time based, so zero to N events
  130. may be fired depending on the actual deltaTime (if deltaTime is
  131. inferior to FixedDeltaTime, no event will be fired, if it is superior
  132. to two times FixedDeltaTime, two events will be fired, etc.).
  133. This option allows to use fixed time steps in simulations (while the
  134. animation and rendering itself may happen at a lower or higher
  135. framerate). *)
  136. property FixedDeltaTime: Double read FFixedDeltaTime write FFixedDeltaTime;
  137. (* Adjusts how progression events are triggered.
  138. See TgxCadencerMode. *)
  139. property Mode: TgxCadencerMode read FMode write SetMode default cmASAP;
  140. (* Allows relinquishing time to other threads/processes.
  141. A "sleep" is issued BEFORE each progress if SleepLength>=0 (see
  142. help for the "sleep" procedure in delphi for details). *)
  143. property SleepLength: Integer read FSleepLength write FSleepLength default -1;
  144. // Happens AFTER scene was progressed.
  145. property OnProgress: TgxProgressEvent read FOnProgress write FOnProgress;
  146. // Happens AFTER all iterations with fixed delta time.
  147. property OnTotalProgress : TgxProgressEvent read FOnTotalProgress write FOnTotalProgress;
  148. end;
  149. // Adds a property to connect/subscribe to a cadencer.
  150. TgxCustomCadencedComponent = class(TgxUpdateAbleComponent)
  151. private
  152. FCadencer: TgxCadencer;
  153. protected
  154. procedure SetCadencer(const val: TgxCadencer);
  155. property Cadencer: TgxCadencer read FCadencer write SetCadencer;
  156. public
  157. destructor Destroy; override;
  158. procedure Notification(AComponent: TComponent; Operation: TOperation);
  159. override;
  160. end;
  161. TgxCadencedComponent = class(TgxCustomCadencedComponent)
  162. published
  163. property Cadencer;
  164. end;
  165. // ---------------------------------------------------------------------
  166. implementation
  167. // ---------------------------------------------------------------------
  168. const
  169. cTickCadencer = 'TickCadencer';
  170. type
  171. // TASAPHandler
  172. TASAPHandler = class
  173. private
  174. FTooFastCounter: Integer;
  175. FTimer: Cardinal;
  176. FWindowHandle: HWND;
  177. procedure WndProc(var Msg: TMessage);
  178. public
  179. constructor Create;
  180. destructor Destroy; override;
  181. end;
  182. var
  183. vWMTickCadencer: Cardinal;
  184. vASAPCadencerList: TList;
  185. vHandler: TASAPHandler;
  186. vCounterFrequency: Int64;
  187. procedure RegisterASAPCadencer(aCadencer: TgxCadencer);
  188. begin
  189. if aCadencer.Mode = cmASAP then
  190. begin
  191. if not Assigned(vASAPCadencerList) then
  192. vASAPCadencerList := TList.Create;
  193. if vASAPCadencerList.IndexOf(aCadencer) < 0 then
  194. begin
  195. vASAPCadencerList.Add(aCadencer);
  196. if not Assigned(vHandler) then
  197. vHandler := TASAPHandler.Create;
  198. end;
  199. end
  200. else if aCadencer.Mode = cmApplicationIdle then
  201. Application.OnIdle := aCadencer.OnIdleEvent;
  202. end;
  203. procedure UnRegisterASAPCadencer(aCadencer: TgxCadencer);
  204. var
  205. i: Integer;
  206. begin
  207. if aCadencer.Mode = cmASAP then
  208. begin
  209. if Assigned(vASAPCadencerList) then
  210. begin
  211. i := vASAPCadencerList.IndexOf(aCadencer);
  212. if i >= 0 then
  213. vASAPCadencerList[i] := nil;
  214. end;
  215. end
  216. else if aCadencer.Mode = cmApplicationIdle then
  217. Application.OnIdle := nil;
  218. end;
  219. // ------------------
  220. // ------------------ TASAPHandler ------------------
  221. // ------------------
  222. constructor TASAPHandler.Create;
  223. begin
  224. inherited Create;
  225. FWindowHandle := AllocateHWnd(WndProc);
  226. PostMessage(FWindowHandle, vWMTickCadencer, 0, 0);
  227. end;
  228. destructor TASAPHandler.Destroy;
  229. begin
  230. if FTimer <> 0 then
  231. KillTimer(FWindowHandle, FTimer);
  232. DeallocateHWnd(FWindowHandle);
  233. inherited Destroy;
  234. end;
  235. var
  236. vWndProcInLoop: Boolean;
  237. procedure TASAPHandler.WndProc(var Msg: TMessage);
  238. var
  239. i: Integer;
  240. cad: TgxCadencer;
  241. begin
  242. // Windows.Beep(440, 10);
  243. with Msg do
  244. begin
  245. if Msg = WM_TIMER then
  246. begin
  247. KillTimer(FWindowHandle, FTimer);
  248. FTimer := 0;
  249. end;
  250. if (Msg <> WM_TIMER) and (Cardinal(GetMessageTime) = GetTickCount) then
  251. begin
  252. // if we're going too fast, "sleep" for 1 msec
  253. Inc(FTooFastCounter);
  254. if FTooFastCounter > 5000 then
  255. begin
  256. if FTimer = 0 then
  257. FTimer := SetTimer(FWindowHandle, 1, 1, nil);
  258. FTooFastCounter := 0;
  259. end;
  260. end
  261. else
  262. FTooFastCounter := 0;
  263. if FTimer <> 0 then
  264. begin
  265. Result := 0;
  266. Exit;
  267. end;
  268. if not vWndProcInLoop then
  269. begin
  270. vWndProcInLoop := True;
  271. try
  272. if (Msg = vWMTickCadencer) or (Msg = WM_TIMER) then
  273. begin
  274. // Progress
  275. for i := vASAPCadencerList.Count - 1 downto 0 do
  276. begin
  277. cad := TgxCadencer(vASAPCadencerList[i]);
  278. if Assigned(cad) and (cad.Mode = cmASAP)
  279. and cad.Enabled and (cad.FProgressing = 0) then
  280. begin
  281. if Application.Terminated then
  282. begin
  283. // force stop
  284. cad.Enabled := False
  285. end
  286. else
  287. begin
  288. try
  289. // do stuff
  290. cad.Progress;
  291. except
  292. Application.HandleException(Self);
  293. // it faulted, stop it
  294. cad.Enabled := False
  295. end
  296. end;
  297. end;
  298. end;
  299. // care for nils
  300. vASAPCadencerList.Pack;
  301. if vASAPCadencerList.Count = 0 then
  302. begin
  303. vASAPCadencerList.Free;
  304. vASAPCadencerList := nil;
  305. vHandler.Free;
  306. vHandler := nil;
  307. end
  308. else
  309. begin
  310. // Prepare the return of the infernal loop...
  311. PostMessage(FWindowHandle, vWMTickCadencer, 0, 0);
  312. end;
  313. end;
  314. finally
  315. vWndProcInLoop := False;
  316. end;
  317. end;
  318. Result := 0;
  319. end;
  320. end;
  321. // ------------------
  322. // ------------------ TgxCadencer ------------------
  323. // ------------------
  324. constructor TgxCadencer.Create(AOwner: TComponent);
  325. begin
  326. inherited Create(AOwner);
  327. FTimeReference := cmPerformanceCounter;
  328. downTime := GetRawReferenceTime;
  329. FOriginTime := downTime;
  330. FTimeMultiplier := 1;
  331. FSleepLength := -1;
  332. Mode := cmASAP;
  333. Enabled := True;
  334. end;
  335. destructor TgxCadencer.Destroy;
  336. begin
  337. Assert(FProgressing = 0);
  338. UnRegisterASAPCadencer(Self);
  339. FSubscribedCadenceableComponents.Free;
  340. FSubscribedCadenceableComponents := nil;
  341. inherited Destroy;
  342. end;
  343. procedure TgxCadencer.Subscribe(aComponent: TgxCadenceAbleComponent);
  344. begin
  345. if not Assigned(FSubscribedCadenceableComponents) then
  346. FSubscribedCadenceableComponents := TList.Create;
  347. if FSubscribedCadenceableComponents.IndexOf(aComponent) < 0 then
  348. begin
  349. FSubscribedCadenceableComponents.Add(aComponent);
  350. aComponent.FreeNotification(Self);
  351. end;
  352. end;
  353. procedure TgxCadencer.UnSubscribe(aComponent: TgxCadenceAbleComponent);
  354. var
  355. i: Integer;
  356. begin
  357. if Assigned(FSubscribedCadenceableComponents) then
  358. begin
  359. i := FSubscribedCadenceableComponents.IndexOf(aComponent);
  360. if i >= 0 then
  361. begin
  362. FSubscribedCadenceableComponents.Delete(i);
  363. aComponent.RemoveFreeNotification(Self);
  364. end;
  365. end;
  366. end;
  367. procedure TgxCadencer.Notification(AComponent: TComponent; Operation:
  368. TOperation);
  369. begin
  370. if Operation = opRemove then
  371. begin
  372. if AComponent = FScene then
  373. FScene := nil;
  374. if Assigned(FSubscribedCadenceableComponents) then
  375. FSubscribedCadenceableComponents.Remove(AComponent);
  376. end;
  377. inherited;
  378. end;
  379. procedure TgxCadencer.Loaded;
  380. begin
  381. inherited Loaded;
  382. RestartASAP;
  383. end;
  384. procedure TgxCadencer.OnIdleEvent(Sender: TObject; var Done: Boolean);
  385. begin
  386. Progress;
  387. Done := False;
  388. end;
  389. procedure TgxCadencer.RestartASAP;
  390. begin
  391. if not (csLoading in ComponentState) then
  392. begin
  393. if (Mode in [cmASAP, cmApplicationIdle]) and (not (csDesigning in
  394. ComponentState))
  395. and Assigned(FScene) and Enabled then
  396. RegisterASAPCadencer(Self)
  397. else
  398. UnRegisterASAPCadencer(Self);
  399. end;
  400. end;
  401. procedure TgxCadencer.SetEnabled(const val: Boolean);
  402. begin
  403. if FEnabled <> val then
  404. begin
  405. FEnabled := val;
  406. if not (csDesigning in ComponentState) then
  407. begin
  408. if Enabled then
  409. FOriginTime := FOriginTime + GetRawReferenceTime - downTime
  410. else
  411. downTime := GetRawReferenceTime;
  412. RestartASAP;
  413. end;
  414. end;
  415. end;
  416. procedure TgxCadencer.SetScene(const val: TgxScene);
  417. begin
  418. if FScene <> val then
  419. begin
  420. if Assigned(FScene) then
  421. FScene.RemoveFreeNotification(Self);
  422. FScene := val;
  423. if Assigned(FScene) then
  424. FScene.FreeNotification(Self);
  425. RestartASAP;
  426. end;
  427. end;
  428. procedure TgxCadencer.SetTimeMultiplier(const val: Double);
  429. var
  430. rawRef: Double;
  431. begin
  432. if val <> FTimeMultiplier then
  433. begin
  434. if val = 0 then
  435. begin
  436. lastMultiplier := FTimeMultiplier;
  437. Enabled := False;
  438. end
  439. else
  440. begin
  441. rawRef := GetRawReferenceTime;
  442. if FTimeMultiplier = 0 then
  443. begin
  444. Enabled := True;
  445. // continuity of time:
  446. // (rawRef-newOriginTime)*val = (rawRef-FOriginTime)*lastMultiplier
  447. FOriginTime := rawRef - (rawRef - FOriginTime) * lastMultiplier / val;
  448. end
  449. else
  450. begin
  451. // continuity of time:
  452. // (rawRef-newOriginTime)*val = (rawRef-FOriginTime)*FTimeMultiplier
  453. FOriginTime := rawRef - (rawRef - FOriginTime) * FTimeMultiplier / val;
  454. end;
  455. end;
  456. FTimeMultiplier := val;
  457. end;
  458. end;
  459. function TgxCadencer.StoreTimeMultiplier: Boolean;
  460. begin
  461. Result := (FTimeMultiplier <> 1);
  462. end;
  463. procedure TgxCadencer.SetMode(const val: TgxCadencerMode);
  464. begin
  465. if FMode <> val then
  466. begin
  467. if FMode <> cmManual then
  468. UnRegisterASAPCadencer(Self);
  469. FMode := val;
  470. RestartASAP;
  471. end;
  472. end;
  473. procedure TgxCadencer.SetTimeReference(const val: TgxCadencerTimeReference);
  474. begin
  475. // nothing more, yet
  476. FTimeReference := val;
  477. end;
  478. procedure TgxCadencer.Progress;
  479. var
  480. deltaTime, newTime, totalDelta: Double;
  481. fullTotalDelta, firstLastTime : Double;
  482. i: Integer;
  483. pt: TgxProgressTimes;
  484. begin
  485. // basic protection against infinite loops,
  486. // shall never happen, unless there is a bug in user code
  487. if FProgressing < 0 then
  488. Exit;
  489. if Enabled then
  490. begin
  491. // avoid stalling everything else...
  492. if SleepLength >= 0 then
  493. Sleep(SleepLength);
  494. // in manual mode, the user is supposed to make sure messages are handled
  495. // in Idle mode, this processing is implicit
  496. if Mode = cmASAP then
  497. begin
  498. Application.ProcessMessages;
  499. if (not Assigned(vASAPCadencerList))
  500. or (vASAPCadencerList.IndexOf(Self) < 0) then
  501. Exit;
  502. end;
  503. end;
  504. Inc(FProgressing);
  505. try
  506. if Enabled then
  507. begin
  508. // One of the processed messages might have disabled us
  509. if Enabled then
  510. begin
  511. // ...and progress !
  512. newTime := GetCurrenttime;
  513. deltaTime := newTime - lastTime;
  514. if (deltaTime >= MinDeltaTime) and (deltaTime >= FixedDeltaTime) then
  515. begin
  516. if FMaxDeltaTime > 0 then
  517. begin
  518. if deltaTime > FMaxDeltaTime then
  519. begin
  520. FOriginTime := FOriginTime + (deltaTime - FMaxDeltaTime) /
  521. FTimeMultiplier;
  522. deltaTime := FMaxDeltaTime;
  523. newTime := lastTime + deltaTime;
  524. end;
  525. end;
  526. totalDelta := deltaTime;
  527. fullTotalDelta := totalDelta;
  528. firstLastTime := lastTime;
  529. if FixedDeltaTime > 0 then
  530. deltaTime := FixedDeltaTime;
  531. while totalDelta >= deltaTime do
  532. begin
  533. lastTime := lastTime + deltaTime;
  534. if Assigned(FScene) and (deltaTime <> 0) then
  535. begin
  536. FProgressing := -FProgressing;
  537. try
  538. FScene.Progress(deltaTime, lastTime);
  539. finally
  540. FProgressing := -FProgressing;
  541. end;
  542. end;
  543. pt.deltaTime := deltaTime;
  544. pt.newTime := lastTime;
  545. i := 0;
  546. while Assigned(FSubscribedCadenceableComponents) and
  547. (i <= FSubscribedCadenceableComponents.Count - 1) do
  548. begin
  549. TgxCadenceAbleComponent(FSubscribedCadenceableComponents[i]).DoProgress(pt);
  550. i := i + 1;
  551. end;
  552. if Assigned(FOnProgress) and (not (csDesigning in ComponentState))
  553. then
  554. FOnProgress(Self, deltaTime, newTime);
  555. if deltaTime <= 0 then
  556. Break;
  557. totalDelta := totalDelta - deltaTime;
  558. end;
  559. if Assigned(FOnTotalProgress)
  560. and (not (csDesigning in ComponentState)) then
  561. FOnTotalProgress(Self, fullTotalDelta, firstLastTime);
  562. end;
  563. end;
  564. end;
  565. finally
  566. Dec(FProgressing);
  567. end;
  568. end;
  569. function TgxCadencer.GetRawReferenceTime: Double;
  570. var
  571. counter: Int64;
  572. begin
  573. case FTimeReference of
  574. cmRTC: // Real Time Clock
  575. Result := Now * (3600 * 24);
  576. cmPerformanceCounter:
  577. begin // HiRes Performance Counter
  578. QueryPerformanceCounter(counter);
  579. Result := counter / vCounterFrequency;
  580. end;
  581. cmExternal: // User defined value
  582. Result := FCurrentTime;
  583. else
  584. Result := 0;
  585. Assert(False);
  586. end;
  587. end;
  588. function TgxCadencer.GetCurrenttime: Double;
  589. begin
  590. Result := (GetRawReferenceTime - FOriginTime) * FTimeMultiplier;
  591. FCurrentTime := Result;
  592. end;
  593. function TgxCadencer.IsBusy: Boolean;
  594. begin
  595. Result := (FProgressing <> 0);
  596. end;
  597. procedure TgxCadencer.Reset;
  598. begin
  599. lasttime := 0;
  600. downTime := GetRawReferenceTime;
  601. FOriginTime := downTime;
  602. end;
  603. procedure TgxCadencer.SetCurrentTime(const Value: Double);
  604. begin
  605. LastTime := Value - (FCurrentTime - LastTime);
  606. FOriginTime := FOriginTime + (FCurrentTime - Value);
  607. FCurrentTime := Value;
  608. end;
  609. // ------------------
  610. // ------------------ TgxCustomCadencedComponent ------------------
  611. // ------------------
  612. destructor TgxCustomCadencedComponent.Destroy;
  613. begin
  614. Cadencer := nil;
  615. inherited Destroy;
  616. end;
  617. procedure TgxCustomCadencedComponent.Notification(AComponent: TComponent;
  618. Operation: TOperation);
  619. begin
  620. if (Operation = opRemove) and (AComponent = FCadencer) then
  621. Cadencer := nil;
  622. inherited;
  623. end;
  624. procedure TgxCustomCadencedComponent.SetCadencer(const val: TgxCadencer);
  625. begin
  626. if FCadencer <> val then
  627. begin
  628. if Assigned(FCadencer) then
  629. FCadencer.UnSubscribe(Self);
  630. FCadencer := val;
  631. if Assigned(FCadencer) then
  632. FCadencer.Subscribe(Self);
  633. end;
  634. end;
  635. // ---------------------------------------------------------------------
  636. initialization
  637. // ---------------------------------------------------------------------
  638. RegisterClasses([TgxCadencer]);
  639. // Get our Windows message ID
  640. vWMTickCadencer := RegisterWindowMessage(cTickCadencer);
  641. // Preparation for high resolution timer
  642. if not QueryPerformanceFrequency(vCounterFrequency) then
  643. vCounterFrequency := 0;
  644. finalization
  645. FreeAndNil(vHandler);
  646. FreeAndNil(vASAPCadencerList);
  647. end.