MSTimer.pas
上传用户:zkjn0718
上传日期:2021-01-01
资源大小:776k
文件大小:8k
源码类别:

Delphi/CppBuilder

开发平台:

Delphi

  1. //
  2. // This unit is part of the GLScene Project, http://glscene.org
  3. // Rewrite and simplify TGLCadencer.
  4. // Wqyfavor    wqyfavor@163.com
  5. unit MSTimer;
  6. interface
  7. uses
  8.    Classes,
  9.    Windows,
  10.    Messages,
  11.    Forms;
  12. type
  13.    TProgressEvent = procedure(const deltaTime, newTime: Double) of object;
  14.    TMSTimer = class
  15.    private
  16.       { Private Declarations }
  17.       FTimeMultiplier: Double;
  18.       lastTime, downTime, lastMultiplier: Double;
  19.       FEnabled: Boolean;
  20.       FSleepLength: Integer;
  21.       FCurrentTime: Double;
  22.       FOriginTime: Double;
  23.       FLastDeltaTime: Double;
  24.       FBaseTime: Double;
  25.       FOnProgress: TProgressEvent;
  26.       FProgressing: Integer;
  27.       procedure SetCurrentTime(const Value: Double);
  28.       function _FGetCurrentTime: Double;
  29.    protected
  30.       { Protected Declarations }
  31.       function StoreTimeMultiplier: Boolean;
  32.       procedure SetEnabled(const val: Boolean);
  33.       procedure SetTimeMultiplier(const val: Double);
  34.       function GetRawReferenceTime: Double;
  35.    public
  36.       { Public Declarations }
  37.       constructor Create;
  38.       destructor Destroy; override;
  39.       procedure Progress;
  40.       function GetCurrentTime: Double;
  41.       function IsBusy: Boolean;
  42.       procedure Reset;
  43.       property BaseTime: Double read FBaseTime write FBaseTime;
  44.       property OriginTime: Double read FOriginTime write FOriginTime;
  45.       property CurrentTime: Double read _FGetCurrentTime write SetCurrentTime;
  46.    published
  47.       property Enabled: Boolean read FEnabled write SetEnabled default False;
  48.       property TimeMultiplier: Double read FTimeMultiplier write SetTimeMultiplier stored StoreTimeMultiplier;
  49.       property LastDeltaTime: Double read FLastDeltaTime write FLastDeltaTime;
  50.       property SleepLength: Integer read FSleepLength write FSleepLength default -1;
  51.       property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
  52.    end;
  53. var
  54.    MSCadencer: TMSTimer = nil;
  55. implementation
  56. uses SysUtils;
  57. var
  58.    vCounterFrequency: Int64;
  59. type
  60.    TASAPHandler = class
  61.       FWindowHandle: HWND;
  62.       FTooFastCounter: Integer;
  63.       FTimer: Cardinal;
  64.       procedure Trigger;
  65.       procedure WndProc(var Message: TMessage);
  66.       constructor Create;
  67.       destructor Destroy; override;
  68.    end;
  69. var
  70.    vWMTickCadencer: Cardinal;
  71.    vHandler: TASAPHandler;
  72. constructor TASAPHandler.Create;
  73. begin
  74.    inherited Create;
  75.    FWindowHandle := AllocateHWnd(WndProc);
  76.    Trigger;
  77. end;
  78. destructor TASAPHandler.Destroy;
  79. begin
  80.    if FTimer <> 0 then
  81.       KillTimer(FWindowHandle, FTimer);
  82.    DeallocateHWnd(FWindowHandle);
  83.    inherited Destroy;
  84. end;
  85. var
  86.    vWndProcInLoop: Boolean;
  87. procedure TASAPHandler.Trigger;
  88. begin
  89.    PostMessage(FWindowHandle, vWMTickCadencer, 0, 0);
  90. end;
  91. procedure TASAPHandler.WndProc(var Message: TMessage);
  92. begin
  93.    with Message do
  94.    begin
  95.       if Msg = WM_TIMER then
  96.       begin
  97.          KillTimer(FWindowHandle, FTimer);
  98.          FTimer := 0;
  99.       end;
  100.       if (Msg <> WM_TIMER) and (Cardinal(GetMessageTime) = GetTickCount) then
  101.       begin
  102.          // if we're going too fast, "sleep" for 1 msec
  103.          Inc(FTooFastCounter);
  104.          if FTooFastCounter > 5000 then
  105.          begin
  106.             if FTimer = 0 then
  107.                FTimer := SetTimer(FWindowHandle, 1, 1, nil);
  108.             FTooFastCounter := 0;
  109.          end;
  110.       end
  111.       else
  112.          FTooFastCounter := 0;
  113.       if FTimer <> 0 then
  114.       begin
  115.          Result := 0;
  116.          Exit;
  117.       end;
  118.       if not vWndProcInLoop then
  119.       begin
  120.          vWndProcInLoop := True;
  121.          try
  122.             if (Msg = vWMTickCadencer) or (Msg = WM_TIMER) then
  123.                if Assigned(MSCadencer) and MSCadencer.Enabled then
  124.                begin
  125.                   if MSCadencer.FProgressing = 0 then
  126.                      if Application.Terminated then
  127.                         MSCadencer.Enabled := False // force stop
  128.                      else
  129.                      begin
  130.                         try
  131.                            MSCadencer.Progress; // do stuff
  132.                         except
  133.                            Application.HandleException(Self);
  134.                            MSCadencer.Enabled := False; // it faulted, stop it
  135.                         end
  136.                      end;
  137.                   Trigger; // Infinite loop...
  138.                end;
  139.          finally
  140.             vWndProcInLoop := False;
  141.          end;
  142.       end;
  143.       Result := 0;
  144.    end;
  145. end;
  146. constructor TMSTimer.Create;
  147. begin
  148.    downTime := GetRawReferenceTime;
  149.    FOriginTime := downTime;
  150.    FTimeMultiplier := 1;
  151.    FSleepLength := -1;
  152.    FBaseTime := 0.0;
  153.    Enabled := False;
  154. end;
  155. destructor TMSTimer.Destroy;
  156. begin
  157.    Assert(FProgressing = 0);
  158.    inherited Destroy;
  159. end;
  160. procedure TMSTimer.SetEnabled(const val: Boolean);
  161. begin
  162.    if FEnabled <> val then
  163.    begin
  164.       FEnabled := val;
  165.       if FEnabled then
  166.       begin
  167.          FOriginTime := FOriginTime + GetRawReferenceTime - downTime;
  168.          vHandler.Trigger;
  169.       end
  170.       else
  171.          downTime := GetRawReferenceTime;
  172.    end;
  173. end;
  174. procedure TMSTimer.SetTimeMultiplier(const val: Double);
  175. var
  176.    rawRef: Double;
  177. begin
  178.    if val <> FTimeMultiplier then
  179.    begin
  180.       if val = 0 then
  181.       begin
  182.          lastMultiplier := FTimeMultiplier;
  183.          Enabled := False;
  184.       end
  185.       else
  186.       begin
  187.          rawRef := GetRawReferenceTime;
  188.          if FTimeMultiplier = 0 then
  189.          begin
  190.             Enabled := True;
  191.             FOriginTime := rawRef - (rawRef - FOriginTime) * lastMultiplier / val;
  192.          end
  193.          else
  194.             FOriginTime := rawRef - (rawRef - FOriginTime) * FTimeMultiplier / val;
  195.       end;
  196.       FTimeMultiplier := val;
  197.    end;
  198. end;
  199. function TMSTimer.StoreTimeMultiplier: Boolean;
  200. begin
  201.    Result := (FTimeMultiplier <> 1);
  202. end;
  203. function TMSTimer._FGetCurrentTime: Double;
  204. begin
  205.    Result := FCurrentTime + FBaseTime;
  206. end;
  207. procedure TMSTimer.Progress;
  208. var
  209.    deltaTime, newTime: Double;
  210. begin
  211.    if FProgressing < 0 then
  212.       Exit;
  213.    if Enabled then
  214.    begin
  215.       if SleepLength >= 0 then
  216.          Sleep(SleepLength);
  217.       Application.ProcessMessages;
  218.    end;
  219.    Inc(FProgressing);
  220.    try
  221.       if Enabled then
  222.       begin
  223.          // One of the processed messages might have disabled us
  224.          if Enabled then
  225.          begin
  226.             newTime := GetCurrentTime;
  227.             deltaTime := newTime - lastTime;
  228.             FLastDeltaTime := deltaTime;
  229.             lastTime := newTime;
  230.             if Assigned(FOnProgress) then
  231.                FOnProgress(deltaTime, newTime + FBaseTime);
  232.          end;
  233.       end;
  234.    finally
  235.       Dec(FProgressing);
  236.    end;
  237. end;
  238. function TMSTimer.GetRawReferenceTime: Double;
  239. var
  240.    counter: Int64;
  241. begin
  242.    QueryPerformanceCounter(counter);
  243.    Result := counter / vCounterFrequency;
  244. end;
  245. function TMSTimer.GetCurrentTime: Double;
  246. begin
  247.    Result := (GetRawReferenceTime - FOriginTime) * FTimeMultiplier;
  248.    FCurrentTime := Result;
  249. end;
  250. function TMSTimer.IsBusy: Boolean;
  251. begin
  252.    Result := (FProgressing <> 0);
  253. end;
  254. procedure TMSTimer.Reset;
  255. begin
  256.    lastTime := 0;
  257.    downTime := GetRawReferenceTime;
  258.    FOriginTime := downTime;
  259.    FBaseTime := 0.0;
  260.    GetCurrentTime;
  261. end;
  262. procedure TMSTimer.SetCurrentTime(const Value: Double);
  263. begin
  264.   { LastTime := Value - (FCurrentTime - LastTime);
  265.    FOriginTime := FOriginTime + (FCurrentTime - Value);
  266.    FCurrentTime := Value;   }
  267.    Reset;
  268.    FBaseTime := Value;
  269. end;
  270. initialization
  271.    // Get our Windows message ID
  272.    vWMTickCadencer := RegisterWindowMessage('TimerTick');
  273.    vHandler := TASAPHandler.Create;
  274.    // Preparation for high resolution timer
  275.    if not QueryPerformanceFrequency(vCounterFrequency) then
  276.       vCounterFrequency := 0;
  277. finalization
  278.    FreeAndNil(vHandler);
  279. end.