123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- //
- // The graphics engine GLScene
- //
- unit GLS.FullScreenViewer;
- (* A Platform specific full-screen viewer *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.Windows,
- Winapi.Messages,
- System.Classes,
- System.SysUtils,
- VCL.Forms,
- VCL.Controls,
- VCL.Menus,
- Stage.OpenGLTokens,
- Stage.Utils,
- GLS.Context,
- GLS.ImageUtils,
- GLS.Scene,
- GLS.SceneViewer;
- type
- TGLScreenDepth = (sd8bits, sd16bits, sd24bits, sd32bits);
- (* A FullScreen viewer.
- This non visual viewer will, when activated, use the full screen as rendering
- surface. It will also switch/restore videomode depending on the required
- width/height.
- This is performed by creating an underlying TForm and using its surface
- for rendering OpenGL, "decent" ICDs will automatically use PageFlipping
- instead of BlockTransfer (slower buffer flipping mode used for windowed OpenGL).
- Note: if you terminate the application either via a kill or in the IDE,
- the original resolution isn't restored. *)
- TGLFullScreenViewer = class(TGLNonVisualViewer)
- private
- FFormIsOwned: Boolean;
- FForm: TForm;
- FOwnDC: HWND;
- FScreenDepth: TGLScreenDepth;
- FActive: Boolean;
- FSwitchedResolution: Boolean;
- FManualRendering: Boolean;
- FUpdateCount: Integer;
- FOnMouseDown: TMouseEvent;
- FOnMouseUp: TMouseEvent;
- FOnMouseMove: TMouseMoveEvent;
- FOnMouseWheel: TMouseWheelEvent;
- FOnMouseWheelDown: TMouseWheelUpDownEvent;
- FOnMouseWheelUp: TMouseWheelUpDownEvent;
- FOnClick, FOnDblClick: TNotifyEvent;
- FOnKeyDown: TKeyEvent;
- FOnKeyUp: TKeyEvent;
- FOnKeyPress: TKeyPressEvent;
- FOnClose: TCloseEvent;
- FOnCloseQuery: TCloseQueryEvent;
- FStayOnTop: Boolean;
- FVSync: TGLVSyncMode;
- FRefreshRate: Integer;
- FCursor: TCursor;
- FPopupMenu: TPopupMenu;
- procedure SetScreenDepth(const val: TGLScreenDepth);
- procedure SetActive(const val: Boolean);
- procedure SetOnMouseDown(const val: TMouseEvent);
- procedure SetOnMouseUp(const val: TMouseEvent);
- procedure SetOnMouseMove(const val: TMouseMoveEvent);
- procedure SetOnMouseWheel(const val: TMouseWheelEvent);
- procedure SetOnMouseWheelDown(const val: TMouseWheelUpDownEvent);
- procedure SetOnMouseWheelUp(const val: TMouseWheelUpDownEvent);
- procedure SetOnClick(const val: TNotifyEvent);
- procedure SetOnDblClick(const val: TNotifyEvent);
- procedure SetOnCloseQuery(const val: TCloseQueryEvent);
- procedure SetOnClose(const val: TCloseEvent);
- procedure SetOnKeyUp(const val: TKeyEvent);
- procedure SetOnKeyDown(const val: TKeyEvent);
- procedure SetOnKeyPress(const val: TKeyPressEvent);
- procedure SetStayOnTop(const val: Boolean);
- procedure SetCursor(const val: TCursor);
- procedure SetPopupMenu(const val: TPopupMenu);
- procedure SetForm(aVal: TForm);
- procedure SetManualRendering(const val: Boolean);
- protected
- function GetHandle: HWND;
- procedure DoBeforeRender(Sender: TObject);
- procedure DoBufferChange(Sender: TObject); override;
- procedure DoBufferStructuralChange(Sender: TObject); override;
- procedure Startup;
- procedure Shutdown;
- procedure BindFormEvents;
- procedure DoCloseQuery(Sender: TObject; var CanClose: Boolean);
- procedure DoPaint(Sender: TObject);
- procedure DoActivate(Sender: TObject);
- procedure DoDeactivate(Sender: TObject);
- procedure DoFormDestroy(Sender: TObject);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Render(baseObject: TGLBaseSceneObject = nil); override;
- (* Adjusts property so that current resolution will be used.
- Call this method if you want to make sure video mode isn't switched. *)
- procedure UseCurrentResolution;
- procedure BeginUpdate;
- procedure EndUpdate;
- // Activates/deactivates full screen mode.
- property Active: Boolean read FActive write SetActive;
- procedure ReActivate;
- (* Read access to the underlying form handle.
- Returns 0 (zero) if the viewer is not active or has not yet
- instantiated its form. *)
- property Handle: HWND read GetHandle;
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- function LastFrameTime: Single;
- function FramesPerSecond: Single;
- function FramesPerSecondText(decimals: Integer = 1): String;
- procedure ResetPerformanceMonitor;
- property RenderDC: HWND read FOwnDC;
- published
- property Form: TForm read FForm write SetForm;
- property ManualRendering: Boolean read FManualRendering write SetManualRendering;
- // It is not used in UNIX. Requested ScreenDepth.
- property ScreenDepth: TGLScreenDepth read FScreenDepth write SetScreenDepth default sd32bits;
- (* Specifies if the underlying form is "fsStayOnTop".
- The benefit of StayOnTop is that it hides the windows bar and
- other background windows. The "fsStayOnTop" is automatically
- switched off/on when the underlying form loses/gains focus.
- It is recommended not to use StayOnTop while running in the IDE
- or during the debugging phase. *)
- property StayOnTop: Boolean read FStayOnTop write SetStayOnTop default False;
- (* Specifies if the refresh should be synchronized with the VSync signal.
- If the underlying OpenGL ICD does not support the WGL_EXT_swap_control
- extension, this property is ignored. *)
- property VSync: TGLVSyncMode read FVSync write FVSync default vsmSync;
- (* Screen refresh rate.
- Use zero for system default. This property allows you to work around
- the winxp bug that limits uses a refresh rate of 60hz when changeing
- resolution. it is however suggested to give the user the opportunity
- to adjust it instead of having a fixed value (expecially beyond
- 75hz or for resolutions beyond 1024x768).
- the value will be automatically clamped to the highest value
- *reported* compatible with the monitor. *)
- property RefreshRate: Integer read FRefreshRate write FRefreshRate;
- property Cursor: TCursor read FCursor write SetCursor default crDefault;
- property PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
- property OnClose: TCloseEvent read FOnClose write SetOnClose;
- property OnKeyUp: TKeyEvent read FOnKeyUp write SetOnKeyUp;
- property OnKeyDown: TKeyEvent read FOnKeyDown write SetOnKeyDown;
- property OnKeyPress: TKeyPressEvent read FOnKeyPress write SetOnKeyPress;
- property OnCloseQuery: TCloseQueryEvent read FOnCloseQuery write SetOnCloseQuery;
- property OnClick: TNotifyEvent read FOnClick write SetOnClick;
- property OnDblClick: TNotifyEvent read FOnDblClick write SetOnDblClick;
- property OnMouseDown: TMouseEvent read FOnMouseDown write SetOnMouseDown;
- property OnMouseUp: TMouseEvent read FOnMouseUp write SetOnMouseUp;
- property OnMouseMove: TMouseMoveEvent read FOnMouseMove write SetOnMouseMove;
- property OnMouseWheel: TMouseWheelEvent read FOnMouseWheel write SetOnMouseWheel;
- property OnMouseWheelDown: TMouseWheelUpDownEvent read FOnMouseWheelDown
- write SetOnMouseWheelDown;
- property OnMouseWheelUp: TMouseWheelUpDownEvent read FOnMouseWheelUp
- write SetOnMouseWheelUp;
- end;
- procedure Register;
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- uses
- GLS.Screen,
- GLS.WindowsContext;
- const
- cScreenDepthToBPP: array [sd8bits .. sd32bits] of Integer = (8, 16, 24, 32);
- procedure Register;
- begin
- RegisterComponents('GLScene', [TGLFullScreenViewer]);
- end;
- // ------------------
- // ------------------ TGLFullScreenViewer ------------------
- // ------------------
- constructor TGLFullScreenViewer.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- Width := 800;
- Height := 600;
- FScreenDepth := sd32bits;
- FVSync := vsmSync;
- FCursor := crDefault;
- Buffer.ViewerBeforeRender := DoBeforeRender;
- end;
- destructor TGLFullScreenViewer.Destroy;
- begin
- Active := False;
- inherited Destroy;
- end;
- procedure TGLFullScreenViewer.DoBeforeRender(Sender: TObject);
- begin
- SetupVSync(VSync);
- end;
- procedure TGLFullScreenViewer.DoBufferChange(Sender: TObject);
- begin
- if Assigned(FForm) and (not Buffer.Rendering) then
- begin
- Buffer.Render;
- end;
- end;
- procedure TGLFullScreenViewer.DoBufferStructuralChange(Sender: TObject);
- begin
- if Active and (FUpdateCount = 0) then
- ReActivate
- end;
- procedure TGLFullScreenViewer.Render(baseObject: TGLBaseSceneObject = nil);
- begin
- Buffer.Render(baseObject);
- end;
- procedure TGLFullScreenViewer.BeginUpdate;
- begin
- Inc(FUpdateCount);
- end;
- procedure TGLFullScreenViewer.EndUpdate;
- begin
- Dec(FUpdateCount);
- if FUpdateCount = 0 then
- begin
- if Active then
- DoBufferStructuralChange(Self)
- end
- else if FUpdateCount < 0 then
- begin
- FUpdateCount := 0;
- Assert(False, 'Unbalanced Begin/EndUpdate');
- end;
- end;
- procedure TGLFullScreenViewer.ReActivate;
- begin
- Shutdown;
- Startup;
- end;
- procedure TGLFullScreenViewer.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- if (Operation = opRemove) and (Buffer <> nil) then
- begin
- if (AComponent = Buffer.Camera) then
- Buffer.Camera := nil;
- Active := False;
- if (AComponent = FForm) then
- begin
- Active := False;
- Form := nil;
- end;
- end;
- inherited Notification(AComponent, Operation);
- end;
- function TGLFullScreenViewer.LastFrameTime: Single;
- begin
- Result := Buffer.LastFrameTime;
- end;
- function TGLFullScreenViewer.FramesPerSecond: Single;
- begin
- Result := Buffer.FramesPerSecond;
- end;
- function TGLFullScreenViewer.FramesPerSecondText(decimals: Integer): String;
- begin
- Result := Format('%.*f FPS', [decimals, Buffer.FramesPerSecond]);
- end;
- procedure TGLFullScreenViewer.ResetPerformanceMonitor;
- begin
- Buffer.ResetPerformanceMonitor;
- end;
- procedure TGLFullScreenViewer.UseCurrentResolution;
- begin
- BeginUpdate;
- try
- Width := Screen.Width;
- Height := Screen.Height;
- case GetCurrentColorDepth of
- 24: ScreenDepth := sd24bits;
- 16: ScreenDepth := sd16bits;
- 8: ScreenDepth := sd8bits;
- else
- // highest depth possible otherwise
- ScreenDepth := sd32bits;
- end;
- finally
- EndUpdate;
- end;
- end;
- procedure TGLFullScreenViewer.SetActive(const val: Boolean);
- begin
- if val <> FActive then
- begin
- // Alt+Tab delayed until better times
- // {$IFDEF MSWindows}
- // Application.OnDeactivate:=DoDeActivate;
- // Application.OnActivate:=DoActivate;
- // {$ENDIF}
- if val then
- Startup
- else
- Shutdown;
- end;
- end;
- procedure TGLFullScreenViewer.Startup;
- var
- res: TResolution;
- begin
- if FActive then
- Exit;
- if FForm = nil then
- begin
- FFormIsOwned := True;
- FForm := TForm.Create(nil);
- FForm.Show();
- end
- else
- FFormIsOwned := False;
- with FForm do
- begin
- If BorderStyle <> bsNone then
- BorderStyle := bsNone;
- Cursor := Self.Cursor;
- PopupMenu := Self.PopupMenu;
- Left := 0;
- Top := 0;
- ClientWidth := Self.Width;
- ClientHeight := Self.Height;
- BindFormEvents;
- res := GetIndexFromResolution(Width, Height,
- cScreenDepthToBPP[ScreenDepth]);
- if res = 0 then
- raise Exception.Create('Unsupported video mode');
- if StayOnTop then
- FormStyle := fsStayOnTop
- else
- FormStyle := fsNormal;
- {$IFDEF MSWINDOWS}
- SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle, GWL_STYLE) and
- not WS_CAPTION);
- {$ENDIF}
- // WindowState:=wsMaximized;
- // Switch video mode
- if (Screen.Width <> Width) or (Screen.Height <> Height) or
- (GetCurrentColorDepth <> cScreenDepthToBPP[ScreenDepth]) then
- begin
- SetFullscreenMode(res, FRefreshRate);
- FSwitchedResolution := True;
- end;
- {$IFDEF MSWINDOWS}
- // Hides Taskbar + Windows 7 Button
- ShowWindow(FindWindow('Shell_TrayWnd', nil), SW_HIDE);
- ShowWindow(FindWindow('BUTTON', nil), SW_HIDE);
- {$ENDIF}
- // Show;
- end;
- Buffer.Resize(0, 0, Width, Height);
- FOwnDC := GetDC(FForm.Handle);
- Buffer.CreateRC(FOwnDC, False);
- // Linux Unicode
- {$IFDEF Linux}
- GrabMouseToForm(FForm);
- {$ENDIF}
- // todo
- FActive := True;
- end;
- procedure TGLFullScreenViewer.Shutdown;
- begin
- if not FActive then
- Exit;
- Assert(FForm <> nil);
- Buffer.DestroyRC;
- with FForm do
- begin
- Cursor := crDefault;
- PopupMenu := nil;
- end;
- {$IFDEF Linux}
- ReleaseMouseFromForm(FForm);
- {$ENDIF}
- {$IFDEF MSWINDOWS}
- // Restore Taskbar + Windows 7 Button
- ShowWindow(FindWindow('Shell_TrayWnd', nil), SW_SHOWNA);
- ShowWindow(FindWindow('BUTTON', nil), SW_SHOWNA);
- {$ENDIF}
- // attempt that, at the very least...
- if FSwitchedResolution then
- RestoreDefaultMode;
- FActive := False;
- if FFormIsOwned then
- FreeAndNil(FForm);
- end;
- procedure TGLFullScreenViewer.BindFormEvents;
- begin
- if Assigned(FForm) then
- with FForm do
- begin
- OnMouseDown := FOnMouseDown;
- OnMouseUp := FOnMouseUp;
- OnMouseMove := FOnMouseMove;
- OnMouseWheel := FOnMouseWheel;
- OnMouseWheelDown := FOnMouseWheelDown;
- OnMouseWheelUp := FOnMouseWheelUp;
- OnClick := FOnClick;
- OnDblClick := FOnDblClick;
- OnCloseQuery := DoCloseQuery;
- OnClose := FOnClose;
- OnKeyUp := FOnKeyUp;
- OnKeyDown := FOnKeyDown;
- OnKeyPress := FOnKeyPress;
- OnPaint := DoPaint;
- OnDestroy := DoFormDestroy;
- end;
- end;
- procedure TGLFullScreenViewer.DoCloseQuery(Sender: TObject;
- var CanClose: Boolean);
- begin
- if Assigned(FOnCloseQuery) then
- FOnCloseQuery(Sender, CanClose);
- CanClose := True;
- // if CanClose then Shutdown;
- end;
- procedure TGLFullScreenViewer.DoPaint(Sender: TObject);
- begin
- If not ManualRendering then
- if Form <> nil then
- Render;
- end;
- procedure TGLFullScreenViewer.DoActivate(Sender: TObject);
- begin
- (* If not Active and (Form <> nil) then begin
- Startup;
- end; *)
- end;
- procedure TGLFullScreenViewer.DoDeactivate(Sender: TObject);
- begin
- (* If Active and (Form <> nil) then begin
- Shutdown;
- Form.Height:=0;
- Form.Width:=0;
- end; *)
- end;
- procedure TGLFullScreenViewer.DoFormDestroy(Sender: TObject);
- begin
- Active := False;
- end;
- procedure TGLFullScreenViewer.SetScreenDepth(const val: TGLScreenDepth);
- begin
- if FScreenDepth <> val then
- begin
- FScreenDepth := val;
- DoBufferStructuralChange(Self);
- end;
- end;
- procedure TGLFullScreenViewer.SetStayOnTop(const val: Boolean);
- begin
- if val <> FStayOnTop then
- begin
- FStayOnTop := val;
- DoBufferStructuralChange(Self);
- end;
- end;
- procedure TGLFullScreenViewer.SetOnCloseQuery(const val: TCloseQueryEvent);
- begin
- FOnCloseQuery := val; // this one uses a special binding
- end;
- procedure TGLFullScreenViewer.SetOnClose(const val: TCloseEvent);
- begin
- If Form <> nil then
- Form.OnClose := val;
- FOnClose := val;
- end;
- procedure TGLFullScreenViewer.SetOnKeyPress(const val: TKeyPressEvent);
- begin
- If Form <> nil then
- Form.OnKeyPress := val;
- FOnKeyPress := val;
- end;
- procedure TGLFullScreenViewer.SetOnKeyUp(const val: TKeyEvent);
- begin
- If Form <> nil then
- Form.OnKeyUp := val;
- FOnKeyUp := val;
- end;
- procedure TGLFullScreenViewer.SetOnKeyDown(const val: TKeyEvent);
- begin
- If Form <> nil then
- Form.OnKeyDown := val;
- FOnKeyDown := val;
- end;
- procedure TGLFullScreenViewer.SetOnMouseWheel(const val: TMouseWheelEvent);
- begin
- If Form <> nil then
- Form.OnMouseWheel := val;
- FOnMouseWheel := val;
- end;
- procedure TGLFullScreenViewer.SetOnMouseWheelDown
- (const val: TMouseWheelUpDownEvent);
- begin
- If Form <> nil then
- Form.OnMouseWheelDown := val;
- FOnMouseWheelDown := val;
- end;
- procedure TGLFullScreenViewer.SetOnMouseWheelUp
- (const val: TMouseWheelUpDownEvent);
- begin
- If Form <> nil then
- Form.OnMouseWheelUp := val;
- FOnMouseWheelUp := val;
- end;
- procedure TGLFullScreenViewer.SetOnClick(const val: TNotifyEvent);
- begin
- If Form <> nil then
- Form.OnClick := val;
- FOnClick := val;
- end;
- procedure TGLFullScreenViewer.SetOnDblClick(const val: TNotifyEvent);
- begin
- If Form <> nil then
- Form.OnDblClick := val;
- FOnDblClick := val;
- end;
- procedure TGLFullScreenViewer.SetOnMouseMove(const val: TMouseMoveEvent);
- begin
- If Form <> nil then
- Form.OnMouseMove := val;
- FOnMouseMove := val;
- end;
- procedure TGLFullScreenViewer.SetOnMouseDown(const val: TMouseEvent);
- begin
- If Form <> nil then
- Form.OnMouseDown := val;
- FOnMouseDown := val;
- end;
- procedure TGLFullScreenViewer.SetOnMouseUp(const val: TMouseEvent);
- begin
- If Form <> nil then
- Form.OnMouseUp := val;
- FOnMouseUp := val;
- end;
- procedure TGLFullScreenViewer.SetCursor(const val: TCursor);
- begin
- if val <> FCursor then
- begin
- FCursor := val;
- if Form <> nil then
- FForm.Cursor := val;
- end;
- end;
- procedure TGLFullScreenViewer.SetPopupMenu(const val: TPopupMenu);
- begin
- if val <> FPopupMenu then
- begin
- FPopupMenu := val;
- if Assigned(FForm) then
- FForm.PopupMenu := val;
- end;
- end;
- procedure TGLFullScreenViewer.SetForm(aVal: TForm);
- begin
- FForm := aVal;
- end;
- procedure TGLFullScreenViewer.SetManualRendering(const val: Boolean);
- begin
- if FManualRendering <> val then
- FManualRendering := val;
- end;
- function TGLFullScreenViewer.GetHandle: HWND;
- begin
- if Form <> nil then
- Result := FForm.Handle
- else
- Result := 0;
- end;
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- RegisterClasses([TGLFullScreenViewer]);
- finalization
- {$IFDEF MSWINDOWS}
- // Restore Taskbar + Windows 7 Button
- ShowWindow(FindWindow('Shell_TrayWnd', nil), SW_SHOWNA);
- ShowWindow(FindWindow('BUTTON', nil), SW_SHOWNA);
- {$ENDIF}
- end.
|