mirror of
https://github.com/salvadordf/CEF4Delphi.git
synced 2024-11-24 08:02:15 +02:00
424 lines
13 KiB
ObjectPascal
424 lines
13 KiB
ObjectPascal
// ************************************************************************
|
|
// ***************************** CEF4Delphi *******************************
|
|
// ************************************************************************
|
|
//
|
|
// CEF4Delphi is based on DCEF3 which uses CEF to embed a chromium-based
|
|
// browser in Delphi applications.
|
|
//
|
|
// The original license of DCEF3 still applies to CEF4Delphi.
|
|
//
|
|
// For more information about CEF4Delphi visit :
|
|
// https://www.briskbard.com/index.php?lang=en&pageid=cef
|
|
//
|
|
// Copyright © 2023 Salvador Diaz Fau. All rights reserved.
|
|
//
|
|
// ************************************************************************
|
|
// ************ vvvv Original license and comments below vvvv *************
|
|
// ************************************************************************
|
|
(*
|
|
* Delphi Chromium Embedded 3
|
|
*
|
|
* Usage allowed under the restrictions of the Lesser GNU General Public License
|
|
* or alternatively the restrictions of the Mozilla Public License 1.1
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
|
* the specific language governing rights and limitations under the License.
|
|
*
|
|
* Unit owner : Henri Gourvest <hgourvest@gmail.com>
|
|
* Web site : http://www.progdigy.com
|
|
* Repository : http://code.google.com/p/delphichromiumembedded/
|
|
* Group : http://groups.google.com/group/delphichromiumembedded
|
|
*
|
|
* Embarcadero Technologies, Inc is not permitted to use or redistribute
|
|
* this source code without explicit permission.
|
|
*
|
|
*)
|
|
|
|
unit uCEFLinuxEventPipe;
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE OBJFPC}{$H+}
|
|
{$ENDIF}
|
|
|
|
{$I cef.inc}
|
|
|
|
{$IFNDEF TARGET_64BITS}{$ALIGN ON}{$ENDIF}
|
|
{$MINENUMSIZE 4}
|
|
|
|
interface
|
|
|
|
{$IFDEF LINUX}
|
|
uses
|
|
Classes, SysUtils, Forms, Unix;
|
|
|
|
type
|
|
gint = integer;
|
|
pgint = ^gint;
|
|
guint = longword;
|
|
gushort = word;
|
|
gboolean = boolean32;
|
|
gpointer = Pointer;
|
|
Pgpointer = ^gpointer;
|
|
PGMainContext = Pointer;
|
|
PGSource = ^TGSource;
|
|
PGSourceFunc = ^TGSourceFunc;
|
|
PGSourceFuncs = ^TGSourceFuncs;
|
|
PGSList = ^TGSList;
|
|
|
|
TGSList = record
|
|
data : gpointer;
|
|
next : PGSList;
|
|
end;
|
|
|
|
TGSourceFunc = function (data:gpointer):gboolean;cdecl;
|
|
|
|
TGSourceDummyMarshal = procedure;cdecl;
|
|
|
|
TGSourceFuncs = record
|
|
prepare : function (source:PGSource; timeout:pgint):gboolean; cdecl;
|
|
check : function (source:PGSource):gboolean; cdecl;
|
|
dispatch : function (source:PGSource; callback:TGSourceFunc; user_data:gpointer):gboolean; cdecl;
|
|
finalize : procedure (source:PGSource); cdecl;
|
|
closure_callback : TGSourceFunc;
|
|
closure_marshal : TGSourceDummyMarshal;
|
|
end;
|
|
|
|
PGSourceCallbackFuncs = ^TGSourceCallbackFuncs;
|
|
TGSourceCallbackFuncs = record
|
|
ref : procedure (cb_data:gpointer); cdecl;
|
|
unref : procedure (cb_data:gpointer); cdecl;
|
|
get : procedure (cb_data:gpointer; source:PGSource; func:PGSourceFunc; data:Pgpointer); cdecl;
|
|
end;
|
|
|
|
TGSource = record
|
|
callback_data : gpointer;
|
|
callback_funcs : PGSourceCallbackFuncs;
|
|
source_funcs : PGSourceFuncs;
|
|
ref_count : guint;
|
|
context : PGMainContext;
|
|
priority : gint;
|
|
flags : guint;
|
|
source_id : guint;
|
|
poll_fds : PGSList;
|
|
prev : PGSource;
|
|
next : PGSource;
|
|
reserved1 : gpointer;
|
|
reserved2 : gpointer;
|
|
end;
|
|
|
|
TCEFLinuxEventPipe = class;
|
|
|
|
PCustomGSource = ^TCustomGSource;
|
|
TCustomGSource = record
|
|
base : TGSource;
|
|
parent : TCEFLinuxEventPipe;
|
|
end;
|
|
|
|
PGPollFD = ^TGPollFD;
|
|
TGPollFD = record
|
|
fd : gint;
|
|
events : gushort;
|
|
revents : gushort;
|
|
end;
|
|
|
|
TOnPrepareEvent = procedure(Sender: TObject; var aTimeout: integer) of object;
|
|
TOnCheckEvent = procedure(Sender: TObject; var aMustDispatch: boolean) of object;
|
|
|
|
TCEFLinuxEventPipe = class
|
|
protected
|
|
FLibHandle : {$IFDEF FPC}TLibHandle{$ELSE}THandle{$ENDIF};
|
|
FWakeupGPollFD : TGPollFD;
|
|
FWorkSource : PCustomGSource;
|
|
FSourceFuncs : TGSourceFuncs;
|
|
FWakeupPipeRead : integer;
|
|
FWakeupPipeWrite : integer;
|
|
FPendingRead : integer;
|
|
FLibName : string;
|
|
FOnPrepare : TOnPrepareEvent;
|
|
FOnCheck : TOnCheckEvent;
|
|
FOnDispatch : TNotifyEvent;
|
|
|
|
function GetInitialized : boolean;
|
|
function GetHasData : boolean;
|
|
function GetHasPendingData : boolean;
|
|
|
|
function DoOnPrepare : integer;
|
|
function DoOnCheck : boolean;
|
|
procedure DoOnDispatch;
|
|
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure InitializePipe;
|
|
function ReadAll(var aValue : integer) : boolean;
|
|
function Read(var aValue : integer) : boolean;
|
|
function Write(aValue : integer) : boolean;
|
|
|
|
property Initialized : boolean read GetInitialized;
|
|
property HasData : boolean read GetHasData;
|
|
property HasPendingData : boolean read GetHasPendingData;
|
|
property LibName : string read FLibName write FLibName;
|
|
property OnPrepare : TOnPrepareEvent read FOnPrepare write FOnPrepare;
|
|
property OnCheck : TOnCheckEvent read FOnCheck write FOnCheck;
|
|
property OnDispatch : TNotifyEvent read FOnDispatch write FOnDispatch;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
implementation
|
|
|
|
{$IFDEF LINUX}
|
|
const
|
|
G_IO_IN = 1;
|
|
G_PRIORITY_DEFAULT_IDLE = 200;
|
|
|
|
var
|
|
g_main_context_default : function:PGMainContext;cdecl;
|
|
g_source_new : function(source_funcs:PGSourceFuncs; struct_size:guint):PGSource;cdecl;
|
|
g_source_add_poll : procedure(source:PGSource; fd:PGPollFD);cdecl;
|
|
g_source_set_priority : procedure(source:PGSource; priority:gint);cdecl;
|
|
g_source_set_can_recurse : procedure(source:PGSource; can_recurse:gboolean);cdecl;
|
|
g_source_attach : function(source:PGSource; context:PGMainContext):guint;cdecl;
|
|
g_source_destroy : procedure(source:PGSource);cdecl;
|
|
g_source_unref : procedure(source:PGSource);cdecl;
|
|
|
|
function WorkSourcePrepare(source:PGSource; timeout:pgint):gboolean; cdecl;
|
|
begin
|
|
timeout^ := PCustomGSource(source)^.parent.DoOnPrepare();
|
|
|
|
// We always return FALSE, so that our timeout is honored. If we were
|
|
// to return TRUE, the timeout would be considered to be 0 and the poll
|
|
// would never block. Once the poll is finished, Check will be called.
|
|
Result := False;
|
|
end;
|
|
|
|
function WorkSourceCheck(source:PGSource):gboolean; cdecl;
|
|
begin
|
|
// Only return TRUE if Dispatch should be called.
|
|
Result := PCustomGSource(source)^.parent.DoOnCheck();
|
|
end;
|
|
|
|
function WorkSourceDispatch(source:PGSource; callback:TGSourceFunc; user_data:gpointer):gboolean; cdecl;
|
|
begin
|
|
PCustomGSource(source)^.parent.DoOnDispatch();
|
|
|
|
// Always return TRUE so our source stays registered.
|
|
Result := True;
|
|
end;
|
|
|
|
|
|
constructor TCEFLinuxEventPipe.Create;
|
|
begin
|
|
inherited Create;
|
|
|
|
FLibName := 'libglib-2.0.so';
|
|
FLibHandle := 0;
|
|
FPendingRead := 0;
|
|
FWorkSource := nil;
|
|
FWakeupPipeRead := 0;
|
|
FWakeupPipeWrite := 0;
|
|
FOnPrepare := nil;
|
|
FOnCheck := nil;
|
|
FOnDispatch := nil;
|
|
|
|
FWakeupGPollFD.fd := 0;
|
|
FWakeupGPollFD.events := 0;
|
|
FWakeupGPollFD.revents := 0;
|
|
|
|
FSourceFuncs.prepare := nil;
|
|
FSourceFuncs.check := nil;
|
|
FSourceFuncs.dispatch := nil;
|
|
FSourceFuncs.finalize := nil;
|
|
FSourceFuncs.closure_callback := nil;
|
|
FSourceFuncs.closure_marshal := nil;
|
|
end;
|
|
|
|
destructor TCEFLinuxEventPipe.Destroy;
|
|
begin
|
|
try
|
|
if assigned(FWorkSource) then
|
|
begin
|
|
g_source_destroy(PGSource(FWorkSource));
|
|
g_source_unref(PGSource(FWorkSource));
|
|
FWorkSource := nil;
|
|
end;
|
|
|
|
if (FWakeupPipeRead <> 0) then
|
|
begin
|
|
FileClose(FWakeupPipeRead);
|
|
FWakeupPipeRead := 0;
|
|
end;
|
|
|
|
if (FWakeupPipeWrite <> 0) then
|
|
begin
|
|
FileClose(FWakeupPipeWrite);
|
|
FWakeupPipeWrite := 0;
|
|
end;
|
|
|
|
if (FLibHandle <> 0) then
|
|
begin
|
|
FreeLibrary(FLibHandle);
|
|
FLibHandle := 0;
|
|
end;
|
|
finally
|
|
inherited Destroy;
|
|
end;
|
|
end;
|
|
|
|
procedure TCEFLinuxEventPipe.InitializePipe;
|
|
var
|
|
TempContext : PGMainContext;
|
|
begin
|
|
{$IFDEF FPC}
|
|
FLibHandle := LoadLibrary(FLibName);
|
|
{$ELSE}
|
|
FLibHandle := LoadLibrary(PChar(FLibName));
|
|
{$ENDIF}
|
|
|
|
if FLibHandle = 0 then
|
|
exit;
|
|
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_main_context_default{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_main_context_default');
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_source_new{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_source_new');
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_source_add_poll{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_source_add_poll');
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_source_set_priority{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_source_set_priority');
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_source_set_can_recurse{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_source_set_can_recurse');
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_source_attach{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_source_attach');
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_source_destroy{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_source_destroy');
|
|
{$IFDEF FPC}Pointer({$ENDIF}g_source_unref{$IFDEF FPC}){$ENDIF} := GetProcAddress(FLibHandle, 'g_source_unref');
|
|
|
|
if assigned(g_main_context_default) and
|
|
assigned(g_source_new) and
|
|
assigned(g_source_add_poll) and
|
|
assigned(g_source_set_priority) and
|
|
assigned(g_source_set_can_recurse) and
|
|
assigned(g_source_attach) and
|
|
assigned(g_source_destroy) and
|
|
assigned(g_source_unref) and
|
|
(AssignPipe(FWakeupPipeRead, FWakeupPipeWrite) <> -1) then
|
|
begin
|
|
TempContext := g_main_context_default();
|
|
|
|
FWakeupGPollFD.fd := FWakeupPipeRead;
|
|
FWakeupGPollFD.events := G_IO_IN;
|
|
|
|
FSourceFuncs.check := {$IFDEF FPC}@{$ENDIF}WorkSourceCheck;
|
|
FSourceFuncs.dispatch := {$IFDEF FPC}@{$ENDIF}WorkSourceDispatch;
|
|
FSourceFuncs.prepare := {$IFDEF FPC}@{$ENDIF}WorkSourcePrepare;
|
|
|
|
FWorkSource := PCustomGSource(g_source_new(@FSourceFuncs, SizeOf(TCustomGSource)));
|
|
FWorkSource^.parent := self;
|
|
|
|
g_source_add_poll(PGSource(FWorkSource), @FWakeupGPollFD);
|
|
g_source_set_priority(PGSource(FWorkSource), G_PRIORITY_DEFAULT_IDLE);
|
|
g_source_set_can_recurse(PGSource(FWorkSource), True);
|
|
|
|
if (g_source_attach(PGSource(FWorkSource), TempContext) <= 0) then
|
|
FWorkSource := nil;
|
|
end;
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.GetInitialized : boolean;
|
|
begin
|
|
Result := (FLibHandle <> 0) and
|
|
(FWakeupPipeRead <> 0) and
|
|
(FWakeupPipeWrite <> 0) and
|
|
assigned(FWorkSource);
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.GetHasData : boolean;
|
|
begin
|
|
Result := ((FWakeupGPollFD.revents and G_IO_IN) <> 0);
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.GetHasPendingData : boolean;
|
|
begin
|
|
Result := (FPendingRead > 0);
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.DoOnPrepare : integer;
|
|
begin
|
|
Result := 0;
|
|
|
|
if assigned(FOnPrepare) then
|
|
FOnPrepare(self, Result);
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.DoOnCheck : boolean;
|
|
begin
|
|
Result := False;
|
|
|
|
if assigned(FOnCheck) then
|
|
FOnCheck(self, Result);
|
|
end;
|
|
|
|
procedure TCEFLinuxEventPipe.DoOnDispatch;
|
|
begin
|
|
if assigned(FOnDispatch) then
|
|
FOnDispatch(self);
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.ReadAll(var aValue : integer) : boolean;
|
|
var
|
|
TempValues : array of integer;
|
|
TempRead : Longint;
|
|
begin
|
|
Result := False;
|
|
aValue := 0;
|
|
|
|
if Initialized and (FPendingRead > 0) then
|
|
try
|
|
SetLength(TempValues, FPendingRead);
|
|
|
|
TempRead := FileRead(FWakeupPipeRead, TempValues[0], SizeOf(integer) * FPendingRead);
|
|
TempRead := pred(TempRead div SizeOf(integer));
|
|
|
|
if (TempRead >= 0) then
|
|
begin
|
|
aValue := TempValues[TempRead];
|
|
FPendingRead := 0;
|
|
Result := True;
|
|
end;
|
|
finally
|
|
Finalize(TempValues);
|
|
end;
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.Read(var aValue : integer) : boolean;
|
|
var
|
|
TempValue : integer;
|
|
TempRead : Longint;
|
|
begin
|
|
Result := False;
|
|
aValue := 0;
|
|
|
|
if Initialized and HasPendingData then
|
|
begin
|
|
TempRead := FileRead(FWakeupPipeRead, TempValue, SizeOf(integer));
|
|
|
|
if (TempRead >= 0) then
|
|
begin
|
|
dec(FPendingRead);
|
|
|
|
aValue := TempValue;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCEFLinuxEventPipe.Write(aValue : integer) : boolean;
|
|
begin
|
|
Result := False;
|
|
|
|
if Initialized and
|
|
(FileWrite(FWakeupPipeWrite, aValue, SizeOf(integer)) > 0) then
|
|
begin
|
|
inc(FPendingRead);
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
end.
|
|
|