2
// This unit is part of the GLScene Engine https://github.com/glscene
6
05/04/11 - Yar - Added property FullScreenVideoMode (thanks to ltyrosine)
7
08/12/10 - Yar - Added code for Lazarus (thanks Rustam Asmandiarov aka Predator)
8
23/08/10 - Yar - Creation
19
{$IFDEF GLS_DELPHI_OR_CPPB}
27
{$IF DEFINED(LCLwin32) or DEFINED(LCLwin64)}
29
WSLCLClasses, Win32Int, Win32WSForms,
30
Win32Proc, LCLMessageGlue, Win32WSControls,
41
{$IFDEF GLS_DELPHI_OR_CPPB}
52
// TGLFullScreenResolution
54
{ Defines how GLSceneForm will handle fullscreen request
55
fcWindowMaximize: Use current resolution (just maximize form and hide OS bars)
56
fcNearestResolution: Change to nearest valid resolution from current window size
57
fcManualResolution: Use FFullScreenVideoMode settings }
58
TGLFullScreenResolution = (
63
// TGLFullScreenVideoMode
65
{ Screen mode settings }
66
TGLFullScreenVideoMode = class(TPersistent)
70
FAltTabSupportEnable: Boolean;
75
FResolutionMode: TGLFullScreenResolution;
76
procedure SetEnabled(aValue: Boolean);
77
procedure SetAltTabSupportEnable(aValue: Boolean);
79
constructor Create(AOwner: TGLSceneForm);
81
property Enabled: Boolean read FEnabled write SetEnabled default False;
82
property AltTabSupportEnable: Boolean read FAltTabSupportEnable
83
write SetAltTabSupportEnable default False;
84
property ResolutionMode: TGLFullScreenResolution read FResolutionMode
85
write FResolutionMode default fcUseCurrent;
86
property Width: Integer read FWidth write FWidth;
87
property Height: Integer read FHeight write FHeight;
88
property ColorDepth: Integer read FColorDepth write FColorDepth;
89
property Frequency: Integer read FFrequency write FFrequency;
94
TGLSceneForm = class(TForm)
97
FBuffer: TGLSceneBuffer;
100
FFullScreenVideoMode: TGLFullScreenVideoMode;
101
procedure SetBeforeRender(const val: TNotifyEvent);
102
function GetBeforeRender: TNotifyEvent;
103
procedure SetPostRender(const val: TNotifyEvent);
104
function GetPostRender: TNotifyEvent;
105
procedure SetAfterRender(const val: TNotifyEvent);
106
function GetAfterRender: TNotifyEvent;
107
procedure SetCamera(const val: TGLCamera);
108
function GetCamera: TGLCamera;
109
procedure SetBuffer(const val: TGLSceneBuffer);
111
function GetFieldOfView: single;
112
procedure SetFieldOfView(const Value: single);
113
function GetIsRenderingContextAvailable: Boolean;
115
procedure LMEraseBkgnd(var Message: TLMEraseBkgnd); message LM_ERASEBKGND;
116
procedure LMPaint(var Message: TLMPaint); message LM_PAINT;
117
procedure LMSize(var Message: TLMSize); message LM_SIZE;
118
procedure LMDestroy(var Message: TLMDestroy); message LM_DESTROY;
119
procedure GetFocus(var Mess: TLMessage); message LM_ACTIVATE;
120
{$IF (lcl_major <= 0) and (lcl_minor <= 9) and (lcl_release < 31)}
121
procedure LastFocus(var Mess: TLMessage); message LM_DEACTIVATE;
124
procedure SetFullScreenVideoMode(AValue: TGLFullScreenVideoMode);
126
procedure ShutdownFS;
129
procedure Notification(AComponent: TComponent; Operation: TOperation);
131
procedure CreateWnd; override;
132
procedure Loaded; override;
134
procedure DoBeforeRender(Sender: TObject); dynamic;
135
procedure DoBufferChange(Sender: TObject); virtual;
136
procedure DoBufferStructuralChange(Sender: TObject); dynamic;
138
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
140
constructor Create(AOwner: TComponent); override;
141
destructor Destroy; override;
142
procedure DestroyWnd; override;
144
property IsRenderingContextAvailable: Boolean read
145
GetIsRenderingContextAvailable;
146
property RenderDC: HDC read FOwnDC;
149
{ Camera from which the scene is rendered. }
150
property Camera: TGLCamera read GetCamera write SetCamera;
152
{ Specifies if the refresh should be synchronized with the VSync signal.
153
If the underlying OpenGL ICD does not support the WGL_EXT_swap_control
154
extension, this property is ignored. }
155
property VSync: TVSyncMode read FVSync write FVSync default vsmNoSync;
157
{ Triggered before the scene's objects get rendered.
158
You may use this event to execute your own OpenGL rendering. }
159
property BeforeRender: TNotifyEvent read GetBeforeRender write
161
{ Triggered just after all the scene's objects have been rendered.
162
The OpenGL context is still active in this event, and you may use it
163
to execute your own OpenGL rendering. }
164
property PostRender: TNotifyEvent read GetPostRender write SetPostRender;
165
{ Called after rendering.
166
You cannot issue OpenGL calls in this event, if you want to do your own
167
OpenGL stuff, use the PostRender event. }
168
property AfterRender: TNotifyEvent read GetAfterRender write SetAfterRender;
170
{ Access to buffer properties. }
171
property Buffer: TGLSceneBuffer read FBuffer write SetBuffer;
173
{ Returns or sets the field of view for the viewer, in degrees.
174
This value depends on the camera and the width and height of the scene.
175
The value isn't persisted, if the width/height or camera.focallength is
176
changed, FieldOfView is changed also. }
177
property FieldOfView: single read GetFieldOfView write SetFieldOfView;
179
property FullScreenVideoMode: TGLFullScreenVideoMode read
181
write SetFullScreenVideoMode;
184
// Code created to workaround black screen and blinking when Manifest is enabled
185
// Код создан для обхода черного экрана и мерцания при включенном Manifest'е
186
{$IF DEFINED(LCLwin32) or DEFINED(LCLwin64)}
188
TGLSOpenGLForm = class(TWin32WSForm)
190
class function CreateHandle(const AWinControl: TWinControl; const AParams:
191
TCreateParams): HWND; override;
194
procedure GLRegisterWSComponent(aControl: TComponentClass);
203
constructor TGLSceneForm.Create(AOwner: TComponent);
205
FBuffer := TGLSceneBuffer.Create(Self);
207
FBuffer.ViewerBeforeRender := DoBeforeRender;
208
FBuffer.OnChange := DoBufferChange;
209
FBuffer.OnStructuralChange := DoBufferStructuralChange;
210
FFullScreenVideoMode := TGLFullScreenVideoMode.Create(Self);
211
inherited Create(AOwner);
214
destructor TGLSceneForm.Destroy;
218
FFullScreenVideoMode.Destroy;
225
procedure TGLSceneForm.Notification(AComponent: TComponent; Operation:
228
if (Operation = opRemove) and (FBuffer <> nil) then
230
if (AComponent = FBuffer.Camera) then
231
FBuffer.Camera := nil;
239
procedure TGLSceneForm.CreateWnd;
242
// initialize and activate the OpenGL rendering context
243
// need to do this only once per window creation as we have a private DC
244
FBuffer.Resize(0, 0, Self.Width, Self.Height);
245
FOwnDC := GetDC(Handle);
246
FBuffer.CreateRC(FOwnDC, false);
252
procedure TGLSceneForm.DestroyWnd;
254
if Assigned(FBuffer) then
259
ReleaseDC(Handle, FOwnDC);
269
procedure TGLSceneForm.Loaded;
272
// initiate window creation
274
if not (csDesigning in ComponentState) then
276
if FFullScreenVideoMode.FEnabled then
281
procedure TGLSceneForm.LMEraseBkgnd(var Message: TLMEraseBkgnd);
283
if IsRenderingContextAvailable then
289
procedure TGLSceneForm.LMPaint(var Message: TLMPaint);
293
BeginPaint(Handle, PS);
295
if IsRenderingContextAvailable and (Width > 0) and (Height > 0) then
298
EndPaint(Handle, PS);
303
procedure TGLSceneForm.LMSize(var Message: TLMSize);
306
if Assigned(FBuffer) then
307
FBuffer.Resize(0, 0, Message.Width, Message.Height);
310
procedure TGLSceneForm.LMDestroy(var Message: TLMDestroy);
312
if Assigned(FBuffer) then
317
ReleaseDC(Handle, FOwnDC);
324
procedure TGLSceneForm.GetFocus(var Mess: TLMessage);
326
if not (csDesigning in ComponentState)
327
and FFullScreenVideoMode.FEnabled
328
and FFullScreenVideoMode.FAltTabSupportEnable then
335
{$IF (lcl_major <= 0) and (lcl_minor <= 9) and (lcl_release < 31)}
336
procedure TGLSceneForm.LastFocus(var Mess: TLMessage);
338
if not (csDesigning in ComponentState)
339
and FFullScreenVideoMode.FEnabled
340
and FFullScreenVideoMode.FAltTabSupportEnable then
350
procedure TGLFullScreenVideoMode.SetEnabled(aValue: Boolean);
352
if FEnabled <> aValue then
355
if not ((csDesigning in FOwner.ComponentState)
356
or (csLoading in FOwner.ComponentState)) then
366
constructor TGLFullScreenVideoMode.Create(AOwner: TGLSceneForm);
371
FAltTabSupportEnable := False;
374
FWidth := vVideoModes[0].Width;
375
FHeight := vVideoModes[0].Height;
376
FColorDepth := vVideoModes[0].ColorDepth;
377
FFrequency := vVideoModes[0].MaxFrequency;
379
{$IFDEF GLS_X11_SUPPORT}
380
FWidth := vVideoModes[0].vdisplay;
381
FHeight := vVideoModes[0].hdisplay;
390
{$Message Hint 'Fullscreen mode not yet implemented for Darwin OSes' }
392
if FFrequency = 0 then
394
FResolutionMode := fcUseCurrent;
397
procedure TGLFullScreenVideoMode.SetAltTabSupportEnable(aValue: Boolean);
399
if FAltTabSupportEnable <> aValue then
400
FAltTabSupportEnable := aValue;
403
procedure TGLSceneForm.StartupFS;
405
case FFullScreenVideoMode.FResolutionMode of
408
SetFullscreenMode(GetIndexFromResolution(ClientWidth, ClientHeight,
410
vVideoModes[0].ColorDepth));
417
SetFullscreenMode(GetIndexFromResolution(FFullScreenVideoMode.Width , FFullScreenVideoMode.Height, FFullScreenVideoMode.ColorDepth), FFullScreenVideoMode.Frequency);
423
BorderStyle := bsNone;
424
FormStyle := fsStayOnTop;
426
WindowState := wsMaximized;
429
ShowInTaskBar := stAlways;
431
{$IFDEF GLS_DELPHI_2009_UP}
432
Application.MainFormOnTaskBar := True;
437
procedure TGLSceneForm.ShutdownFS;
441
WindowState := wsNormal;
442
BorderStyle := bsSingle;
443
FormStyle := fsNormal;
444
Left := (Screen.Width div 2) - (Width div 2);
445
Top := (Screen.Height div 2) - (Height div 2);
451
procedure TGLSceneForm.DoBeforeRender(Sender: TObject);
459
procedure TGLSceneForm.DoBufferChange(Sender: TObject);
461
if (not Buffer.Rendering) and (not Buffer.Freezed) then
465
// DoBufferStructuralChange
468
procedure TGLSceneForm.DoBufferStructuralChange(Sender: TObject);
474
procedure TGLSceneForm.MouseMove(Shift: TShiftState; X, Y: Integer);
477
if csDesignInteractive in ControlStyle then
478
FBuffer.NotifyMouseMove(Shift, X, Y);
484
procedure TGLSceneForm.SetBeforeRender(const val: TNotifyEvent);
486
FBuffer.BeforeRender := val;
492
function TGLSceneForm.GetBeforeRender: TNotifyEvent;
494
Result := FBuffer.BeforeRender;
500
procedure TGLSceneForm.SetPostRender(const val: TNotifyEvent);
502
FBuffer.PostRender := val;
508
function TGLSceneForm.GetPostRender: TNotifyEvent;
510
Result := FBuffer.PostRender;
516
procedure TGLSceneForm.SetAfterRender(const val: TNotifyEvent);
518
FBuffer.AfterRender := val;
524
function TGLSceneForm.GetAfterRender: TNotifyEvent;
526
Result := FBuffer.AfterRender;
532
procedure TGLSceneForm.SetCamera(const val: TGLCamera);
534
FBuffer.Camera := val;
540
function TGLSceneForm.GetCamera: TGLCamera;
542
Result := FBuffer.Camera;
548
procedure TGLSceneForm.SetBuffer(const val: TGLSceneBuffer);
556
function TGLSceneForm.GetFieldOfView: single;
558
if not Assigned(Camera) then
560
else if Width < Height then
561
Result := Camera.GetFieldOfView(Width)
563
Result := Camera.GetFieldOfView(Height);
569
procedure TGLSceneForm.SetFieldOfView(const Value: single);
571
if Assigned(Camera) then
573
if Width < Height then
574
Camera.SetFieldOfView(Value, Width)
576
Camera.SetFieldOfView(Value, Height);
580
procedure TGLSceneForm.SetFullScreenVideoMode(AValue: TGLFullScreenVideoMode);
584
// GetIsRenderingContextAvailable
587
function TGLSceneForm.GetIsRenderingContextAvailable: Boolean;
589
Result := FBuffer.RCInstantiated and FBuffer.RenderingContext.IsValid;
592
{$IF DEFINED(LCLwin32) or DEFINED(LCLwin64)}
595
function GlWindowProc(Window: HWND; Msg: UInt; wParam: Windows.wParam; LParam:
596
Windows.LParam): LResult; stdcall;
599
winctrl: TWinControl;
608
winctrl := GetWin32WindowInfo(Window)^.WinControl;
609
if Assigned(winctrl) then
611
FillChar(PaintMsg, SizeOf(PaintMsg), 0);
612
PaintMsg.Msg := LM_PAINT;
613
PaintMsg.DC := wParam;
614
DeliverMessage(winctrl, PaintMsg);
615
Result := PaintMsg.Result;
618
Result := WindowProc(Window, Msg, wParam, LParam);
621
Result := WindowProc(Window, Msg, wParam, LParam);
627
function GetDesigningBorderStyle(const AForm: TCustomForm): TFormBorderStyle;
629
if csDesigning in AForm.ComponentState then
632
Result := AForm.BorderStyle;
635
function CalcBorderIconsFlags(const AForm: TCustomForm): DWORD;
637
BorderIcons: TBorderIcons;
640
BorderIcons := AForm.BorderIcons;
641
if (biSystemMenu in BorderIcons) or (csDesigning in AForm.ComponentState) then
642
Result := Result or WS_SYSMENU;
644
if GetDesigningBorderStyle(AForm) in [bsNone, bsSingle, bsSizeable] then
646
if biMinimize in BorderIcons then
647
Result := Result or WS_MINIMIZEBOX;
648
if biMaximize in BorderIcons then
649
Result := Result or WS_MAXIMIZEBOX;
653
function CalcBorderIconsFlagsEx(const AForm: TCustomForm): DWORD;
655
BorderIcons: TBorderIcons;
658
BorderIcons := AForm.BorderIcons;
659
if GetDesigningBorderStyle(AForm) in [bsSingle, bsSizeable, bsDialog] then
661
if biHelp in BorderIcons then
662
Result := Result or WS_EX_CONTEXTHELP;
666
function CalcBorderStyleFlags(const AForm: TCustomForm): DWORD;
668
Result := WS_CLIPCHILDREN or WS_CLIPSIBLINGS;
669
case GetDesigningBorderStyle(AForm) of
670
bsSizeable, bsSizeToolWin:
671
Result := Result or (WS_OVERLAPPED or WS_THICKFRAME or WS_CAPTION);
672
bsSingle, bsToolWindow:
673
Result := Result or (WS_OVERLAPPED or WS_BORDER or WS_CAPTION);
675
Result := Result or (WS_POPUP or WS_BORDER or WS_CAPTION);
677
if (AForm.Parent = nil) and (AForm.ParentWindow = 0) then
678
Result := Result or WS_POPUP;
682
function CalcBorderStyleFlagsEx(const AForm: TCustomForm): DWORD;
685
case GetDesigningBorderStyle(AForm) of
687
Result := WS_EX_DLGMODALFRAME or WS_EX_WINDOWEDGE;
688
bsToolWindow, bsSizeToolWin:
689
Result := WS_EX_TOOLWINDOW;
693
procedure CalcFormWindowFlags(const AForm: TCustomForm; var Flags, FlagsEx:
696
Flags := CalcBorderStyleFlags(AForm);
697
if AForm.Parent <> nil then
698
Flags := (Flags or WS_CHILD) and not WS_POPUP;
699
// clear border style flags
700
FlagsEx := FlagsEx and not (WS_EX_DLGMODALFRAME or WS_EX_WINDOWEDGE or
702
// set border style flags
703
FlagsEx := FlagsEx or CalcBorderStyleFlagsEx(AForm);
704
if (AForm.FormStyle in fsAllStayOnTop) and not (csDesigning in
705
AForm.ComponentState) then
706
FlagsEx := FlagsEx or WS_EX_TOPMOST;
707
Flags := Flags or CalcBorderIconsFlags(AForm);
708
FlagsEx := FlagsEx or CalcBorderIconsFlagsEx(AForm);
711
procedure AdjustFormBounds(const AForm: TCustomForm; var SizeRect: TRect);
713
// the LCL defines the size of a form without border, win32 with.
714
// -> adjust size according to BorderStyle
715
SizeRect := AForm.BoundsRect;
716
AdjustWindowRectEx(SizeRect, CalcBorderStyleFlags(AForm),
717
false, CalcBorderStyleFlagsEx(AForm));
720
{$IF DEFINED(LCLwin32) or DEFINED(LCLwin64)}
721
class function TGLSOpenGLForm.CreateHandle(const AWinControl: TWinControl; const
722
AParams: TCreateParams): HWND;
724
Params: TCreateWindowExParams;
725
lForm: TCustomForm absolute AWinControl;
729
// general initialization of Params
730
{$IF (lcl_major = 0) and (lcl_release <= 28) }
731
PrepareCreateWindow(AWinControl, Params);
733
PrepareCreateWindow(AWinControl, AParams, Params);
735
// customization of Params
738
CalcFormWindowFlags(lForm, Flags, FlagsEx);
739
pClassName := @ClsName;
740
WindowTitle := StrCaption;
741
AdjustFormBounds(lForm, Bounds);
742
if (lForm.Position in [poDefault, poDefaultPosOnly]) and not (csDesigning in
743
lForm.ComponentState) then
745
Left := CW_USEDEFAULT;
746
Top := CW_USEDEFAULT;
753
if (lForm.Position in [poDefault, poDefaultSizeOnly]) and not (csDesigning in
754
lForm.ComponentState) then
756
Width := CW_USEDEFAULT;
757
Height := CW_USEDEFAULT;
761
Width := Bounds.Right - Bounds.Left;
762
Height := Bounds.Bottom - Bounds.Top;
764
SubClassWndProc := @GlWindowProc;
765
if not (csDesigning in lForm.ComponentState) and lForm.AlphaBlend then
766
FlagsEx := FlagsEx or WS_EX_LAYERED;
768
SetStdBiDiModeParams(AWinControl, Params);
770
FinishCreateWindow(AWinControl, Params, false);
771
Result := Params.Window;
773
// remove system menu items for bsDialog
774
if (lForm.BorderStyle = bsDialog) and not (csDesigning in lForm.ComponentState)
777
SystemMenu := GetSystemMenu(Result, false);
778
DeleteMenu(SystemMenu, SC_RESTORE, MF_BYCOMMAND);
779
DeleteMenu(SystemMenu, SC_SIZE, MF_BYCOMMAND);
780
DeleteMenu(SystemMenu, SC_MINIMIZE, MF_BYCOMMAND);
781
DeleteMenu(SystemMenu, SC_MAXIMIZE, MF_BYCOMMAND);
782
DeleteMenu(SystemMenu, 1, MF_BYPOSITION);
783
// remove the separator between move and close
786
// Beginning with Windows 2000 the UI in an application may hide focus
787
// rectangles and accelerator key indication. According to msdn we need to
788
// initialize all root windows with this message
789
if WindowsVersion >= wv2000 then
790
Windows.SendMessage(Result, WM_CHANGEUISTATE, MakeWParam(UIS_INITIALIZE,
791
UISF_HIDEFOCUS or UISF_HIDEACCEL), 0)
794
procedure GLRegisterWSComponent(aControl: TComponentClass);
796
RegisterWSComponent(aControl, TGLSOpenGLForm);
800
// ------------------------------------------------------------------
801
// ------------------------------------------------------------------
802
// ------------------------------------------------------------------
805
// ------------------------------------------------------------------
806
// ------------------------------------------------------------------
807
// ------------------------------------------------------------------
809
RegisterClass(TGLSceneForm);
811
{$IF DEFINED(LCLwin32) or DEFINED(LCLwin64)}
812
// Code created to workaround black screen and blinking when Manifest is enabled
813
// You may comment it for Win2000\98
814
// Код создан для обхода черного экрана и мерцания при включенном Manifest'е
815
// Можно закоментировать для Win2000\98
816
GLRegisterWSComponent(TGLSceneForm);