mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-06 15:32:50 +02:00
387 lines
11 KiB
ObjectPascal
387 lines
11 KiB
ObjectPascal
{
|
|
*****************************************************************************
|
|
* *
|
|
* See the file COPYING.modifiedLGPL, included in this distribution, *
|
|
* for details about the copyright. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
|
* *
|
|
*****************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
}
|
|
unit GTKGLAreaControl;
|
|
|
|
{$MODE objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, LCLProc, LCLType, LCLIntf,
|
|
{$IFDEF VER1_0}Linux{$ELSE}Unix{$ENDIF},
|
|
Forms, Controls, Graphics, LMessages, InterfaceBase, WSLCLClasses, WSControls,
|
|
LResources, GTKInt, Gtk, NVGL,
|
|
{$IFDEF UseGtkGlAreaLib}
|
|
GTKGLArea_Int
|
|
{$ELSE}
|
|
OpenGLGtkWidget
|
|
{$ENDIF}
|
|
;
|
|
|
|
type
|
|
TGtkGlAreaMakeCurrentEvent = procedure(Sender: TObject;
|
|
var Allow: boolean) of object;
|
|
|
|
{ TCustomGTKGLAreaControl }
|
|
|
|
TCustomGTKGLAreaControl = class(TWinControl)
|
|
private
|
|
FCanvas: TCanvas; // only valid at designtime
|
|
FOnMakeCurrent: TGtkGlAreaMakeCurrentEvent;
|
|
FOnPaint: TNotifyEvent;
|
|
FCurrentFrameTime: integer; // in msec
|
|
FLastFrameTime: integer; // in msec
|
|
FFrameDiffTime: integer; // in msec
|
|
FSharedArea: TCustomGTKGLAreaControl;
|
|
FSharingAreas: TList;
|
|
function GetSharingAreas(Index: integer): TCustomGTKGLAreaControl;
|
|
procedure SetSharedArea(const AValue: TCustomGTKGLAreaControl);
|
|
protected
|
|
procedure WMPaint(var Message: TLMPaint); message LM_PAINT;
|
|
function GetWidget: PGtkGLArea;
|
|
procedure UpdateFrameTimeDiff;
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
Procedure Paint; virtual;
|
|
procedure RealizeBounds; override;
|
|
procedure DoOnPaint; virtual;
|
|
procedure SwapBuffers; virtual;
|
|
function MakeCurrent: boolean; virtual;
|
|
function RestoreOldGtkGLAreaControl: boolean;
|
|
function SharingAreasCount: integer;
|
|
property SharingAreas[Index: integer]: TCustomGTKGLAreaControl read GetSharingAreas;
|
|
public
|
|
property FrameDiffTimeInMSecs: integer read FFrameDiffTime;
|
|
property OnMakeCurrent: TGtkGlAreaMakeCurrentEvent read FOnMakeCurrent
|
|
write FOnMakeCurrent;
|
|
property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
|
|
property SharedArea: TCustomGTKGLAreaControl read FSharedArea write SetSharedArea;
|
|
property Widget: PGtkGLArea read GetWidget;
|
|
end;
|
|
|
|
|
|
{ TGTKGLAreaControl }
|
|
|
|
TGTKGLAreaControl = class(TCustomGTKGLAreaControl)
|
|
published
|
|
property Align;
|
|
property Anchors;
|
|
property BorderSpacing;
|
|
property Enabled;
|
|
property OnClick;
|
|
property OnConstrainedResize;
|
|
property OnDblClick;
|
|
property OnEnter;
|
|
property OnExit;
|
|
property OnKeyDown;
|
|
property OnKeyPress;
|
|
property OnKeyUp;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnPaint;
|
|
property OnResize;
|
|
property OnShowHint;
|
|
property PopupMenu;
|
|
property SharedArea;
|
|
property ShowHint;
|
|
property Visible;
|
|
end;
|
|
|
|
|
|
{ TWSGTKGLAreaControl }
|
|
|
|
TWSGTKGLAreaControl = class(TWSWinControl)
|
|
public
|
|
class function CreateHandle(const AWinControl: TWinControl;
|
|
const AParams: TCreateParams): HWND; override;
|
|
end;
|
|
|
|
|
|
function GetCurrentGtkGLAreaControl: TGTKGLAreaControl;
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
const
|
|
InitAttrList: array [1..11] of LongInt = (
|
|
GDK_GL_RGBA,
|
|
GDK_GL_RED_SIZE, 1,
|
|
GDK_GL_GREEN_SIZE, 1,
|
|
GDK_GL_BLUE_SIZE, 1,
|
|
GDK_GL_DEPTH_SIZE,1,
|
|
GDK_GL_DOUBLEBUFFER,
|
|
GDK_GL_None
|
|
);
|
|
|
|
var
|
|
GtkGLAreaControlStack: TList;
|
|
|
|
function GetCurrentGtkGLAreaControl: TGTKGLAreaControl;
|
|
begin
|
|
if (GtkGLAreaControlStack<>nil)
|
|
and (GtkGLAreaControlStack.Count>0) then
|
|
Result:=TGTKGLAreaControl(GtkGLAreaControlStack[GtkGLAreaControlStack.Count-1])
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('OpenGL',[TGTKGLAreaControl]);
|
|
end;
|
|
|
|
{ TCustomGTKGLAreaControl }
|
|
|
|
constructor TCustomGTKGLAreaControl.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
ControlStyle:=ControlStyle-[csSetCaption];
|
|
if (csDesigning in ComponentState) then begin
|
|
FCanvas := TControlCanvas.Create;
|
|
TControlCanvas(FCanvas).Control := Self;
|
|
end else
|
|
FCompStyle:=csNonLCL;
|
|
SetInitialBounds(0, 0, 160, 90);
|
|
end;
|
|
|
|
destructor TCustomGTKGLAreaControl.Destroy;
|
|
begin
|
|
if FSharingAreas<>nil then begin
|
|
while SharingAreasCount>0 do
|
|
SharingAreas[SharingAreasCount-1].SharedArea:=nil;
|
|
FreeAndNil(FSharingAreas);
|
|
end;
|
|
SharedArea:=nil;
|
|
FCanvas.Free;
|
|
FCanvas:=nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCustomGTKGLAreaControl.Paint;
|
|
begin
|
|
if (not (csDesigning in ComponentState))
|
|
and Enabled and Visible and HandleAllocated
|
|
and MakeCurrent then begin
|
|
try
|
|
UpdateFrameTimeDiff;
|
|
DoOnPaint;
|
|
finally
|
|
RestoreOldGtkGLAreaControl;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGTKGLAreaControl.RealizeBounds;
|
|
var
|
|
RestoreNeeded: Boolean;
|
|
begin
|
|
RestoreNeeded:=false;
|
|
if (not (csDesigning in ComponentState))
|
|
and Enabled and Visible and HandleAllocated
|
|
and MakeCurrent then begin
|
|
RestoreNeeded:=true;
|
|
glViewport (0, 0, Width, Height);
|
|
end;
|
|
try
|
|
inherited RealizeBounds;
|
|
finally
|
|
if RestoreNeeded then
|
|
RestoreOldGtkGLAreaControl;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomGTKGLAreaControl.DoOnPaint;
|
|
begin
|
|
if Assigned(OnPaint) then OnPaint(Self);
|
|
end;
|
|
|
|
procedure TCustomGTKGLAreaControl.SwapBuffers;
|
|
begin
|
|
gtk_gl_area_swap_buffers(Widget);
|
|
end;
|
|
|
|
function TCustomGTKGLAreaControl.MakeCurrent: boolean;
|
|
var
|
|
Allowed: Boolean;
|
|
begin
|
|
if Assigned(FOnMakeCurrent) then begin
|
|
Allowed:=true;
|
|
OnMakeCurrent(Self,Allowed);
|
|
if not Allowed then begin
|
|
Result:=False;
|
|
exit;
|
|
end;
|
|
end;
|
|
// make sure the widget is realized
|
|
gtk_widget_realize(PGtkWidget(Widget));
|
|
// make current
|
|
Result:=gtk_gl_area_make_current(Widget)
|
|
{$IFDEF UseGtkGlAreaLib}=gint(True){$ENDIF};
|
|
if Result then begin
|
|
// on success push on stack
|
|
if GtkGLAreaControlStack=nil then
|
|
GtkGLAreaControlStack:=TList.Create;
|
|
GtkGLAreaControlStack.Add(Self);
|
|
end;
|
|
end;
|
|
|
|
function TCustomGTKGLAreaControl.RestoreOldGtkGLAreaControl: boolean;
|
|
var
|
|
RestoredControl: TGTKGLAreaControl;
|
|
begin
|
|
Result:=false;
|
|
// check if the current context is on stack
|
|
if (GtkGLAreaControlStack=nil) or (GtkGLAreaControlStack.Count=0) then exit;
|
|
// pop
|
|
GtkGLAreaControlStack.Delete(GtkGLAreaControlStack.Count-1);
|
|
// make old control the current control
|
|
if GtkGLAreaControlStack.Count>0 then begin
|
|
RestoredControl:=
|
|
TGTKGLAreaControl(GtkGLAreaControlStack[GtkGLAreaControlStack.Count-1]);
|
|
{$IFDEF UseGtkGlAreaLib}
|
|
if gtk_gl_area_make_current(RestoredControl.Widget)<>gint(true) then
|
|
{$ELSE}
|
|
if not gtk_gl_area_make_current(RestoredControl.Widget) then
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCustomGTKGLAreaControl.SharingAreasCount: integer;
|
|
begin
|
|
if FSharingAreas=nil then
|
|
Result:=0
|
|
else
|
|
Result:=FSharingAreas.Count;
|
|
end;
|
|
|
|
procedure TCustomGTKGLAreaControl.SetSharedArea(
|
|
const AValue: TCustomGTKGLAreaControl);
|
|
begin
|
|
if FSharedArea=AValue then exit;
|
|
if AValue=Self then
|
|
Raise Exception.Create('An area can not be shared by itself.');
|
|
// unshare old
|
|
if (AValue<>nil) and (AValue.SharedArea<>nil) then
|
|
Raise Exception.Create('Target area is sharing too. A sharing area can not be shared.');
|
|
if FSharedArea<>nil then FSharedArea.FSharingAreas.Remove(Self);
|
|
// share new
|
|
if (AValue<>nil) and (csDestroying in AValue.ComponentState) then
|
|
FSharedArea:=nil
|
|
else begin
|
|
FSharedArea:=AValue;
|
|
if (FSharedArea<>nil) then begin
|
|
if FSharedArea.FSharingAreas=nil then
|
|
FSharedArea.FSharingAreas:=TList.Create;
|
|
FSharedArea.FSharingAreas.Add(Self);
|
|
end;
|
|
end;
|
|
// recreate handle if needed
|
|
if HandleAllocated and (not (csDesigning in ComponentState)) then
|
|
ReCreateWnd(Self);
|
|
end;
|
|
|
|
function TCustomGTKGLAreaControl.GetSharingAreas(Index: integer
|
|
): TCustomGTKGLAreaControl;
|
|
begin
|
|
Result:=TCustomGTKGLAreaControl(FSharingAreas[Index]);
|
|
end;
|
|
|
|
procedure TCustomGTKGLAreaControl.WMPaint(var Message: TLMPaint);
|
|
begin
|
|
Include(FControlState, csCustomPaint);
|
|
inherited WMPaint(Message);
|
|
//debugln('TCustomGTKGLAreaControl.WMPaint A ',dbgsName(Self),' ',dbgsName(FCanvas));
|
|
if (csDesigning in ComponentState) and (FCanvas<>nil) then begin
|
|
with FCanvas do begin
|
|
Brush.Color:=clLtGray;
|
|
Pen.Color:=clRed;
|
|
Rectangle(0,0,Self.Width-1,Self.Height-1);
|
|
MoveTo(0,0);
|
|
LineTo(Self.Width,Self.Height);
|
|
MoveTo(0,Self.Height);
|
|
LineTo(Self.Width,0);
|
|
end;
|
|
end else begin
|
|
Paint;
|
|
end;
|
|
Exclude(FControlState, csCustomPaint);
|
|
end;
|
|
|
|
function TCustomGTKGLAreaControl.GetWidget: PGtkGLArea;
|
|
begin
|
|
if HandleAllocated then
|
|
Result:=PGtkGLArea(Handle)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TCustomGTKGLAreaControl.UpdateFrameTimeDiff;
|
|
begin
|
|
FCurrentFrameTime:=GetTickCount;
|
|
if FLastFrameTime=0 then
|
|
FLastFrameTime:=FCurrentFrameTime;
|
|
// calculate time since last call:
|
|
FFrameDiffTime:=FCurrentFrameTime-FLastFrameTime;
|
|
// if the counter is reset restart:
|
|
if (FFrameDiffTime<0) then FFrameDiffTime:=1;
|
|
FLastFrameTime:=FCurrentFrameTime;
|
|
end;
|
|
|
|
{ TWSGTKGLAreaControl }
|
|
|
|
function TWSGTKGLAreaControl.CreateHandle(const AWinControl: TWinControl;
|
|
const AParams: TCreateParams): HWND;
|
|
var
|
|
NewWidget: Pointer;
|
|
Area: TCustomGTKGLAreaControl;
|
|
begin
|
|
//debugln('TWSGTKGLAreaControl.CreateHandle A AWinControl=',dbgsName(AWinControl),' csDesigning=',dbgs(csDesigning in AWinControl.ComponentState));
|
|
if csDesigning in AWinControl.ComponentState then
|
|
Result:=inherited CreateHandle(AWinControl,AParams)
|
|
else begin
|
|
Area:=AWinControl as TCustomGTKGLAreaControl;
|
|
if (Area.SharedArea<>nil) and (not (csDestroying in Area.ComponentState))
|
|
then
|
|
NewWidget:=gtk_gl_area_share_new(Plongint(@InitAttrList),
|
|
Area.SharedArea.Widget)
|
|
else
|
|
NewWidget:=gtk_gl_area_new(Plongint(@InitAttrList));
|
|
Result:=longint(NewWidget);
|
|
TGTKWidgetSet(InterfaceObject).FinishComponentCreate(AWinControl,NewWidget,
|
|
true);
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
{$i gtkglarea.lrs}
|
|
GtkGLAreaControlStack:=nil;
|
|
RegisterWSComponent(TCustomGTKGLAreaControl,TWSGTKGLAreaControl);
|
|
|
|
finalization
|
|
FreeAndNil(GtkGLAreaControlStack);
|
|
|
|
end.
|