CEF4Delphi/source/uCEFTimerWorkScheduler.pas
salvadordf ca8bc9dff4 Added cef4delphi.chm help file
Added the PDS file to extract the HTML Help files using PasDoc
Added more XML documentation
Fixed some XML errors.
Removed the license copy from the pas units.
Updated the LICENSE.md file
2023-08-09 19:38:57 +02:00

405 lines
9.6 KiB
ObjectPascal

unit uCEFTimerWorkScheduler;
{$IFDEF FPC}
{$MODE OBJFPC}{$H+}
{$ENDIF}
{$I cef.inc}
{.$DEFINE USEEVENTPIPE}
{$IFNDEF TARGET_64BITS}{$ALIGN ON}{$ENDIF}
{$MINENUMSIZE 4}
interface
uses
{$IFDEF DELPHI16_UP}
System.Classes, System.SyncObjs, {$IFDEF MSWINDOWS}WinApi.Windows, WinApi.Messages,{$ENDIF}
{$IFDEF FMX}
FMX.Types, uCEFMacOSCustomCocoaTimer,
{$ELSE}
Vcl.ExtCtrls,
{$ENDIF}
{$ELSE}
Classes, SyncObjs, {$IFDEF MSWINDOWS}Windows,{$ENDIF} ExtCtrls,
{$IFDEF FPC}
LMessages, Forms,
{$ELSE}
Messages,
{$ENDIF}
{$ENDIF}
{$IFDEF USEEVENTPIPE}uCEFLinuxEventPipe,{$ENDIF} uCEFTypes, uCEFConstants,
uCEFApplicationCore;
type
TOnAllowEvent = procedure(Sender: TObject; var allow : boolean) of object;
TCEFTimerWorkScheduler = class
protected
FTimer : {$IFDEF MACOS}TCustomCocoaTimer{$ELSE}TTimer{$ENDIF};
FDepleteWorkCycles : cardinal;
FDepleteWorkDelay : cardinal;
FStopped : boolean;
FIsActive : boolean;
FReentrancyDetected : boolean;
FOnAllowDoWork : TOnAllowEvent;
{$IFDEF USEEVENTPIPE}
FEventPipe : TCEFLinuxEventPipe;
FDelayedWorkTime : TCefTime;
{$ENDIF}
{$IFDEF MSWINDOWS}
FCompHandle : HWND;
{$ENDIF}
function GetIsTimerPending : boolean;
procedure Timer_OnTimer(Sender: TObject);
{$IFDEF USEEVENTPIPE}
procedure FEventPipe_OnPrepare(Sender: TObject; var aTimeout: integer);
procedure FEventPipe_OnCheck(Sender: TObject; var aMustDispatch: boolean);
procedure FEventPipe_OnDispatch(Sender: TObject);
{$ENDIF}
procedure Initialize;
procedure CreateTimer;
procedure DestroyTimer;
procedure KillTimer;
procedure SetTimer(aInterval : integer);
procedure DoWork;
function PerformMessageLoopWork : boolean;
procedure DoMessageLoopWork;
procedure OnScheduleWork(delay_ms : integer);
procedure DepleteWork;
{$IFDEF MSWINDOWS}
procedure WndProc(var aMessage: TMessage);
procedure AllocateWindowHandle;
procedure DeallocateWindowHandle;
{$ELSE}
{$IFDEF FPC}
procedure OnScheduleWorkAsync(Data: PtrInt);
{$ENDIF}
{$ENDIF}
public
constructor Create;
destructor Destroy; override;
procedure StopScheduler;
procedure ScheduleMessagePumpWork(const delay_ms : int64);
property DepleteWorkCycles : cardinal read FDepleteWorkCycles write FDepleteWorkCycles;
property DepleteWorkDelay : cardinal read FDepleteWorkDelay write FDepleteWorkDelay;
property IsTimerPending : boolean read GetIsTimerPending;
property OnAllowDoWork : TOnAllowEvent read FOnAllowDoWork write FOnAllowDoWork;
end;
var
GlobalCEFTimerWorkScheduler : TCEFTimerWorkScheduler = nil;
procedure DestroyGlobalCEFTimerWorkScheduler;
implementation
uses
{$IFDEF DELPHI16_UP}
System.SysUtils, System.Math, {$IFDEF MACOS}System.RTTI, FMX.Forms, FMX.Platform,{$ENDIF}
{$ELSE}
SysUtils, Math,
{$ENDIF}
uCEFMiscFunctions;
procedure DestroyGlobalCEFTimerWorkScheduler;
begin
if (GlobalCEFTimerWorkScheduler <> nil) then
FreeAndNil(GlobalCEFTimerWorkScheduler);
end;
constructor TCEFTimerWorkScheduler.Create;
begin
inherited Create;
Initialize;
{$IFDEF MSWINDOWS}
AllocateWindowHandle;
{$ENDIF}
{$IFDEF USEEVENTPIPE}
FEventPipe := TCEFLinuxEventPipe.Create;
FEventPipe.OnPrepare := {$IFDEF FPC}@{$ENDIF}FEventPipe_OnPrepare;
FEventPipe.OnCheck := {$IFDEF FPC}@{$ENDIF}FEventPipe_OnCheck;
FEventPipe.OnDispatch := {$IFDEF FPC}@{$ENDIF}FEventPipe_OnDispatch;
FEventPipe.InitializePipe;
{$ENDIF}
end;
destructor TCEFTimerWorkScheduler.Destroy;
begin
DestroyTimer;
{$IFDEF MSWINDOWS}
DeallocateWindowHandle;
{$ENDIF}
{$IFDEF USEEVENTPIPE}
if assigned(FEventPipe) then
FreeAndNil(FEventPipe);
{$ENDIF}
inherited Destroy;
end;
procedure TCEFTimerWorkScheduler.Initialize;
begin
{$IFDEF MSWINDOWS}
FCompHandle := 0;
{$ENDIF}
{$IFDEF USEEVENTPIPE}
FEventPipe := nil;
InitializeCefTime(FDelayedWorkTime);
{$ENDIF}
FOnAllowDoWork := nil;
FTimer := nil;
FStopped := False;
FIsActive := False;
FReentrancyDetected := False;
FDepleteWorkCycles := CEF_TIMER_DEPLETEWORK_CYCLES;
FDepleteWorkDelay := CEF_TIMER_DEPLETEWORK_DELAY;
end;
{$IFDEF MSWINDOWS}
procedure TCEFTimerWorkScheduler.WndProc(var aMessage: TMessage);
begin
if (aMessage.Msg = CEF_PUMPHAVEWORK) then
OnScheduleWork(aMessage.lParam)
else
aMessage.Result := DefWindowProc(FCompHandle, aMessage.Msg, aMessage.WParam, aMessage.LParam);
end;
procedure TCEFTimerWorkScheduler.AllocateWindowHandle;
begin
if (FCompHandle = 0) and (GlobalCEFApp <> nil) and
((GlobalCEFApp.ProcessType = ptBrowser) or GlobalCEFApp.SingleProcess) then
FCompHandle := AllocateHWnd({$IFDEF FPC}@{$ENDIF}WndProc);
end;
procedure TCEFTimerWorkScheduler.DeallocateWindowHandle;
begin
if (FCompHandle <> 0) then
begin
DeallocateHWnd(FCompHandle);
FCompHandle := 0;
end;
end;
{$ENDIF}
{$IFDEF USEEVENTPIPE}
procedure TCEFTimerWorkScheduler.FEventPipe_OnPrepare(Sender: TObject; var aTimeout: integer);
begin
aTimeout := GetTimeIntervalMilliseconds(FDelayedWorkTime);
end;
procedure TCEFTimerWorkScheduler.FEventPipe_OnCheck(Sender: TObject; var aMustDispatch: boolean);
var
TempValue : integer;
begin
if FEventPipe.HasData or FEventPipe.HasPendingData then
begin
TempValue := 0;
FEventPipe.Read(TempValue);
OnScheduleWork(TempValue);
end;
aMustDispatch := GetTimeIntervalMilliseconds(FDelayedWorkTime) = 0;
end;
procedure TCEFTimerWorkScheduler.FEventPipe_OnDispatch(Sender: TObject);
begin
KillTimer;
DoWork;
end;
{$ENDIF}
procedure TCEFTimerWorkScheduler.StopScheduler;
begin
FStopped := True;
KillTimer;
DepleteWork;
end;
procedure TCEFTimerWorkScheduler.DepleteWork;
var
i : cardinal;
begin
i := FDepleteWorkCycles;
while (i > 0) do
begin
DoMessageLoopWork;
Sleep(FDepleteWorkDelay);
dec(i);
end;
end;
{$IFNDEF MSWINDOWS}{$IFDEF FPC}
procedure TCEFTimerWorkScheduler.OnScheduleWorkAsync(Data: PtrInt);
begin
OnScheduleWork(integer(Data));
end;
{$ENDIF}{$ENDIF}
procedure TCEFTimerWorkScheduler.CreateTimer;
begin
if (FTimer = nil) then
begin
{$IFDEF MACOS}
FTimer := TCustomCocoaTimer.Create;
{$ELSE}
FTimer := TTimer.Create(nil);
{$ENDIF}
FTimer.OnTimer := {$IFDEF FPC}@{$ENDIF}Timer_OnTimer;
FTimer.Enabled := False;
end;
end;
procedure TCEFTimerWorkScheduler.DestroyTimer;
begin
if (FTimer <> nil) then
FreeAndNil(FTimer);
end;
procedure TCEFTimerWorkScheduler.KillTimer;
begin
{$IFDEF USEEVENTPIPE}
InitializeCefTime(FDelayedWorkTime);
{$ELSE}
if (FTimer <> nil) then
FTimer.Enabled := False;
{$ENDIF}
end;
procedure TCEFTimerWorkScheduler.SetTimer(aInterval : integer);
begin
{$IFDEF USEEVENTPIPE}
FDelayedWorkTime := DoubleToCefTime((DoubleTimeNow + aInterval) / 1000);
{$ELSE}
if (FTimer = nil) then
CreateTimer;
FTimer.Interval := aInterval;
FTimer.Enabled := True;
{$ENDIF}
end;
function TCEFTimerWorkScheduler.GetIsTimerPending : boolean;
begin
{$IFDEF USEEVENTPIPE}
Result := GetTimeIntervalMilliseconds(FDelayedWorkTime) > 0;
{$ELSE}
Result := (FTimer <> nil) and FTimer.Enabled;
{$ENDIF}
end;
procedure TCEFTimerWorkScheduler.OnScheduleWork(delay_ms : integer);
begin
if FStopped or
((delay_ms = high(integer)) and IsTimerPending) then
exit;
KillTimer;
if (delay_ms <= 0) then
DoWork
else
SetTimer(min(delay_ms, CEF_TIMER_MAXDELAY));
end;
procedure TCEFTimerWorkScheduler.Timer_OnTimer(Sender: TObject);
begin
KillTimer;
DoWork;
end;
procedure TCEFTimerWorkScheduler.DoWork;
begin
if PerformMessageLoopWork then
ScheduleMessagePumpWork(0)
else
if not(IsTimerPending) then
ScheduleMessagePumpWork(high(integer));
end;
function TCEFTimerWorkScheduler.PerformMessageLoopWork : boolean;
begin
Result := False;
if FIsActive then
begin
FReentrancyDetected := True;
exit;
end;
FReentrancyDetected := False;
DoMessageLoopWork;
Result := FReentrancyDetected;
end;
procedure TCEFTimerWorkScheduler.DoMessageLoopWork;
var
TempAllow : boolean;
begin
TempAllow := True;
if assigned(FOnAllowDoWork) then
FOnAllowDoWork(self, TempAllow);
if TempAllow and (GlobalCEFApp <> nil) then
try
FIsActive := True;
GlobalCEFApp.DoMessageLoopWork;
finally
FIsActive := False;
end;
end;
procedure TCEFTimerWorkScheduler.ScheduleMessagePumpWork(const delay_ms : int64);
begin
if FStopped then exit;
{$IFDEF MSWINDOWS}
if (FCompHandle <> 0) then
PostMessage(FCompHandle, CEF_PUMPHAVEWORK, 0, LPARAM(delay_ms));
{$ENDIF}
{$IFDEF LINUX}
{$IFDEF FPC}
{$IFDEF USEEVENTPIPE}
if assigned(FEventPipe) then
FEventPipe.Write(integer(delay_ms));
{$ELSE}
Application.QueueAsyncCall(@OnScheduleWorkAsync, integer(delay_ms));
{$ENDIF}
{$ELSE}
TThread.ForceQueue(nil, procedure
begin
OnScheduleWork(integer(delay_ms));
end);
{$ENDIF}
{$ENDIF}
{$IFDEF MACOS}
{$IFDEF FPC}
Application.QueueAsyncCall(@OnScheduleWorkAsync, integer(delay_ms));
{$ELSE}
TThread.ForceQueue(nil, procedure
begin
OnScheduleWork(integer(delay_ms));
end);
{$ENDIF}
{$ENDIF}
end;
end.