Class TGLWindow

DescriptionHierarchyFieldsMethodsProperties

Unit

Declaration

type TGLWindow = class(TObject)

Description

Small helpful utilities for GLWindow implementations that are based on a system where left shift and right shift keys (or Alt, or Ctrl keys) are given as different keys. This situation requires some special care because in our TKey type (and so in TGLWindow.KeysDown table) we do not differentiate between right and left Shift key (or Alt, or Ctrl).

Currently this concerns GTK and Xlib implementations.

If you want to use this file in some GLWindow implementation, define GLWINDOW_USE_PRIVATE_MODIFIERS_DOWN when compiling that implementation. It is important to define GLWINDOW_USE_PRIVATE_MODIFIERS_DOWN instead of simply including this file in GLWindow-implementation-specific file, because this allows ReleaseAllKeysAndMouse to use some special code when GLWINDOW_USE_PRIVATE_MODIFIERS_DOWN.

Then you have to call SetPrivateModifiersDown with appropriate arguments at appropriate times. And that's all. SetPrivateModifiersDown will take care of calling DoKeyDown(k) and DoKeyUp(k) for you for k = K_Shift, K_Ctrl, K_Alt.

Hierarchy

Overview

Fields

Public OnInitList: TDynGLWindowFuncArray;
Public MinWidth: integer;
Public MinHeight: integer;
Public MaxWidth: integer;
Public MaxHeight: integer;
Public AccumBufferBits: TVector4Cardinal;
Public OnCloseList: TDynGLWindowFuncArray;
Public OnKeyDown: TKeyCharFunc;
Public OwnsMainMenu: boolean;
Public OnMenuCommand: TMenuCommandFunc;
Public UserData: Pointer;
Public KeysDown: TKeysBooleans;

Methods

Protected function MakeGLAreaContainer(GLArea: PGtkGLArea): PGtkWidget; virtual;
Public procedure EventResize; virtual;
Public procedure EventInit; virtual;
Public procedure EventClose; virtual;
Public function EventCloseQuery: boolean; virtual;
Public procedure EventDraw; virtual;
Public procedure EventBeforeDraw; virtual;
Public procedure EventKeyDown(Key: TKey; c: char); virtual;
Public procedure EventKeyUp(key: TKey); virtual;
Public procedure EventMouseMove(newX, newY: integer); virtual;
Public procedure EventMouseDown(btn: TMouseButton); virtual;
Public procedure EventMouseUp(btn: TMouseButton); virtual;
Public procedure EventIdle; virtual;
Public procedure EventTimer; virtual;
Public procedure EventMenuCommand(Item: TMenuItem); virtual;
Public function AllowsProcessMessageSuspend: boolean; virtual;
Public procedure SetMousePosition(const NewMouseX, NewMouseY: Integer);
Public procedure Init;
Public procedure Close(QuitWhenLastWindowClosed: boolean = true);
Public procedure PostRedisplay;
Public procedure FlushRedisplay;
Public procedure MakeCurrent;
Public procedure SaveScreen(const fname: string); overload;
Public function SaveScreen: TRGBImage; overload;
Public function SaveScreen( const xpos, ypos, SavedAreaWidth, SavedAreaHeight: integer): TRGBImage; overload;
Public function SaveScreenToDisplayList: TGLuint; overload;
Public function SaveScreenToDisplayList(const xpos, ypos, SavedAreaWidth, SavedAreaHeight: integer): TGLuint; overload;
Public procedure SaveScreenDialog(ProposedFileName: string);
Public function GetCallbacksState: TGLWindowCallbacks;
Public procedure SetCallbacksState(const Callbacks: TGLWindowCallbacks);
Public constructor Create;
Public destructor Destroy; override;
Public function ModifiersDown: TModifierKeys;
Public function FpsFrameTime: Double;
Public function FpsRealTime: Double;
Public procedure FpsToCaption(const WindowTitle: string);
Public procedure FpsReset;
Public procedure InitLoop; overload;
Public procedure InitLoop(const ACaption: string; AOnDraw: TDrawFunc); overload;
Public procedure ParseParameters(); overload;
Public procedure ParseParameters(const AllowedOptions: TGLWindowParseOptions); overload;
Public procedure ParseParameters(const AllowedOptions: TGLWindowParseOptions; out SpecifiedOptions: TGLWindowParseOptions); overload;
Public class function ParseParametersHelp( const AllowedOptions: TGLWindowParseOptions; AddHeader: boolean): string;
Public function FileDialog(const Title: string; var FileName: string; OpenDialog: boolean): boolean;
Public function ColorDialog(var Color: TVector3Single): boolean;

Properties

Public property BorderWidth: Cardinal read FBorderWidth write FBorderWidth default 0;
Public property Width: integer read FWidth write FWidth default GLWindowDefaultSize;
Public property Height: integer read FHeight write FHeight default GLWindowDefaultSize;
Public property Left: integer read FLeft write FLeft;
Public property Top : integer read FTop write FTop;
Public property FullScreen: boolean read FFullScreen write FFullScreen;
Public property DoubleBuffer: boolean read FDoubleBuffer write FDoubleBuffer default true;
Public property ColorBits: integer read FColorBits write FColorBits default 0;
Public property Cursor: TGLWindowCursor read FCursor write SetCursor default gcDefault;
Public property CustomCursor: TAlphaImage read FCustomCursor write SetCustomCursor;
Public property ResizeAllowed: TResizeAllowed read FResizeAllowed write FResizeAllowed;
Public property OnInit: TGLWindowFunc read FOnInit write FOnInit;
Public property DepthBufferBits: Cardinal read FDepthBufferBits write FDepthBufferBits default DefaultDepthBufferBits;
Public property StencilBufferBits: Cardinal read FStencilBufferBits write FStencilBufferBits default 0;
Public property MultiSampling: Cardinal read FMultiSampling write FMultiSampling default 1;
Public property AlphaBits: Cardinal read FAlphaBits write FAlphaBits default 0;
Public property Caption: string read FCaption write SetCaption;
Public property OnDraw: TDrawFunc read FOnDraw write FOnDraw;
Public property OnBeforeDraw: TDrawFunc read FOnBeforeDraw write FOnBeforeDraw;
Public property OnResize: TGLWindowFunc read FOnResize write FOnResize;
Public property OnClose: TGLWindowFunc read FOnClose write FOnClose;
Public property OnKeyUp: TKeyFunc read FOnKeyUp write FOnKeyUp;
Public property OnCloseQuery: TGLWindowFunc read FOnCloseQuery write FOnCloseQuery;
Public property OnMouseMove : TMouseMoveFunc read FMouseMove write FMouseMove;
Public property OnMouseDown : TMouseUpDownFunc read FMouseDown write FMouseDown ;
Public property OnMouseUp : TMouseUpDownFunc read FMouseUp write FMouseUp ;
Public property OnIdle: TGLWindowFunc read FOnIdle write FOnIdle;
Public property OnTimer: TGLWindowFunc read FOnTimer write FOnTimer;
Public property AutoRedisplay: boolean read fAutoRedisplay write SetAutoRedisplay;
Public property MainMenu: TMenu read FMainMenu write SetMainMenu;
Public property MousePressed: TMouseButtons read FMousePressed;
Public property MouseX: integer read FMouseX;
Public property MouseY: integer read FMouseY;
Public property Closed: boolean read FClosed;
Public property FpsActive: boolean read FFpsActive write SetFpsActive;
Public property FpsSecondsToAutoReset: Cardinal read FFpsSecondsToAutoReset write FFpsSecondsToAutoReset;
Public property FpsHoldsAfterReset: DWORD read FFpsHoldsAfterReset write FFpsHoldsAfterReset;
Public property FpsCompSpeed: Single read FFpsCompSpeed;
Public property IdleCompSpeed: Single read FIdleCompSpeed;

Description

Fields

Public OnInitList: TDynGLWindowFuncArray;

zawsze po wywolaniu OnInit beda wywolywane wszystkie funkcje z listy OnInitList. Ten obiekt jest tylko tworzony i niszczony w tej klasie, poza tym mozesz na nim robic co ci sie zywnie podoba - dodawac, usuwac, przegladac procedury, co chcesz. Oczywiscie najbardziej przewidywalne jest zachowanie programu ktory tylko dodaje do tej listy.

Public MinWidth: integer;

minimalne i maksymalne rozmiary okna. Musi byc 0 < minWidth <= maxWidth, 0 < minHeight <= maxHeight.

Jesli sprobujesz samemu zainicjowac Width lub Height okienka na cos spoza tego zakresu - jesli to bedzie mniejsze od minWidth to zostanie przyjete minWidth, jesli wieksze od maxWidth - zostanie przyjete maxWidth (tzn. zostanie poprawione dopiero w Init !). Bedzie to wykonane nawet jesli ResizeAllowed = raNotAllowed ! Wiec pamietaj ze jesli chcesz zeby ResizeAllowed = raNotAllowed bylo honorowane - width i height musza sie zawierac w min/max Width/Height. Podobnie, jezeli ustawisz Fullscreen := true i okaze sie ze rozmiary ekranu sa zle - flaga FullScreen zostanie wylaczona. Innymi slowy, niniejsze ograniczenia maja priorytet ponad ResizeAllowed, a wszystkie razem (min/maxWidth/Height i ResizeAllowed) maja priorytet nad Width, Height, FullScreen.

Tym sposobem ZAWSZE bedzie zachodzic minWidth <= width <= maxWidth, o ile tylko not Closed. I wszystko co napisalem dziala tak samo dla Height.

Public MinHeight: integer;

minimalne i maksymalne rozmiary okna. Musi byc 0 < minWidth <= maxWidth, 0 < minHeight <= maxHeight.

Jesli sprobujesz samemu zainicjowac Width lub Height okienka na cos spoza tego zakresu - jesli to bedzie mniejsze od minWidth to zostanie przyjete minWidth, jesli wieksze od maxWidth - zostanie przyjete maxWidth (tzn. zostanie poprawione dopiero w Init !). Bedzie to wykonane nawet jesli ResizeAllowed = raNotAllowed ! Wiec pamietaj ze jesli chcesz zeby ResizeAllowed = raNotAllowed bylo honorowane - width i height musza sie zawierac w min/max Width/Height. Podobnie, jezeli ustawisz Fullscreen := true i okaze sie ze rozmiary ekranu sa zle - flaga FullScreen zostanie wylaczona. Innymi slowy, niniejsze ograniczenia maja priorytet ponad ResizeAllowed, a wszystkie razem (min/maxWidth/Height i ResizeAllowed) maja priorytet nad Width, Height, FullScreen.

Tym sposobem ZAWSZE bedzie zachodzic minWidth <= width <= maxWidth, o ile tylko not Closed. I wszystko co napisalem dziala tak samo dla Height.

Public MaxWidth: integer;

minimalne i maksymalne rozmiary okna. Musi byc 0 < minWidth <= maxWidth, 0 < minHeight <= maxHeight.

Jesli sprobujesz samemu zainicjowac Width lub Height okienka na cos spoza tego zakresu - jesli to bedzie mniejsze od minWidth to zostanie przyjete minWidth, jesli wieksze od maxWidth - zostanie przyjete maxWidth (tzn. zostanie poprawione dopiero w Init !). Bedzie to wykonane nawet jesli ResizeAllowed = raNotAllowed ! Wiec pamietaj ze jesli chcesz zeby ResizeAllowed = raNotAllowed bylo honorowane - width i height musza sie zawierac w min/max Width/Height. Podobnie, jezeli ustawisz Fullscreen := true i okaze sie ze rozmiary ekranu sa zle - flaga FullScreen zostanie wylaczona. Innymi slowy, niniejsze ograniczenia maja priorytet ponad ResizeAllowed, a wszystkie razem (min/maxWidth/Height i ResizeAllowed) maja priorytet nad Width, Height, FullScreen.

Tym sposobem ZAWSZE bedzie zachodzic minWidth <= width <= maxWidth, o ile tylko not Closed. I wszystko co napisalem dziala tak samo dla Height.

Public MaxHeight: integer;

minimalne i maksymalne rozmiary okna. Musi byc 0 < minWidth <= maxWidth, 0 < minHeight <= maxHeight.

Jesli sprobujesz samemu zainicjowac Width lub Height okienka na cos spoza tego zakresu - jesli to bedzie mniejsze od minWidth to zostanie przyjete minWidth, jesli wieksze od maxWidth - zostanie przyjete maxWidth (tzn. zostanie poprawione dopiero w Init !). Bedzie to wykonane nawet jesli ResizeAllowed = raNotAllowed ! Wiec pamietaj ze jesli chcesz zeby ResizeAllowed = raNotAllowed bylo honorowane - width i height musza sie zawierac w min/max Width/Height. Podobnie, jezeli ustawisz Fullscreen := true i okaze sie ze rozmiary ekranu sa zle - flaga FullScreen zostanie wylaczona. Innymi slowy, niniejsze ograniczenia maja priorytet ponad ResizeAllowed, a wszystkie razem (min/maxWidth/Height i ResizeAllowed) maja priorytet nad Width, Height, FullScreen.

Tym sposobem ZAWSZE bedzie zachodzic minWidth <= width <= maxWidth, o ile tylko not Closed. I wszystko co napisalem dziala tak samo dla Height.

Public AccumBufferBits: TVector4Cardinal;

Required number of bits in color channels of accumulation buffer. Color channel is 0..3: red, green, blue, alpha. Zero means that given channel of accumulation buffer is not needed, so when the vector is all zeros (default value) this means that accumulation buffer is not needed at all.

Just like with other XxxBufferBits property, we may get more bits than we requested. But we will never get less — if window system will not be able to provide GL context with requested number of bits, Init will raise an error.

Public OnCloseList: TDynGLWindowFuncArray;

podobnie jak OnInit, OnClose tez ma swoja wersje listowa : OnCloseList. Patrz komentarze przy OnInitList.

Public OnKeyDown: TKeyCharFunc;

OnKeyDown(Self, Key, c) occurs when user presses some key that we can represent as key: TKey and/or c: char.

Not all keyboard keys can be represented as TKey value. There are some keys that generate sensible char values, but still cannot be represented as TKey value, e.g. key '/' does not have any K_Xxx constant for now but can be expressed as char '/'. So you can get Key = K_None is such situations, e.g. OnKeyDown will be called like OnKeyDown(Self, K_None, '/').

Character c is based on pressed key, current Modifiers state, state of keys like "Caps-Lock" , maybe some OS configurarion (like locale-specific chars, e.g. polish "ogonki"), etc. In general, it is operating-system (and window-system, and GLWindow-backend) specific. Not all key presses are representable as char, so you may get c = #0 in such situations. E.g. "up arrow" key does not have a corresponding char code, so OnKeyDown may be called like OnKeyDown(Self, K_Up, #0).

Never will both Key = K_None and c = #0 (this would be rather useless event...). (Unless you will explicitely call EventKeyDown(K_None, #0), which you should never do.)

Note: once I had here separate events, OnKeyPress (with only c: char) and OnKeyDown (with only Key: TKey). But this was very error-prone: for one user key press you could get two events (e.g. OnKeyDown(K_C) and then OnKeyPress('c')). Problems with this were easily avoidable in small programs (where you can see all your OnKeyDown and OnKeyPress handlers in one file), but in large programs they were producing very nasty bugs. E.g. imagine that you handle in OnKeyDown key K_Enter by doing GLWinMessages.MessageOK. But then each time user presses Enter key you 1. handle it in OnKeyDown calling GLWinMessages.MessageOK 2. GLWinMessages.MessageOK changes your GLWindow callbacks so that OnKeyPress(#13) makes GLWinMessages.MessageOK exit. 3. but then you're getting OnKeyPress(#13) event (because K_Enter is converted to #13 char). So GLWinMessages.MessageOK ends. This looked like a bug in GLWinMessages.MessageOK. But actually it was a bug in callbacks design: you were getting two callbacks (OnKeyDown amd OnKeyPress) for one event (user presses a key).

polish: KeyDown moze byc pod wplywem key repeata. Tzn. ze jesli uzytkownik bedzie trzymal klawisz przycisniety to my mozemy dostawac co chwila nowe KeyDown. Pod niektorymi systemami moze byc wtedy tak ze przed kazdym KeyDown bedziemy dostawali "udawane" KeyUp (udawane, bo user tak naprawde ani na chwile nie puscil klawisza) a pod niektorymi nie bedziemy dostawali KeyUp (bedziemy po prostu dostawali KeyDown(k) podczas gdy juz wczesniej KeysDown[k] = true). Piszac program musisz byc gotow na obie mozliwosci. Podsumowujac : nie patrz na KeyDown(k) jako na "zdarzenie ktore zachodzi gdy KeysDown[k] zmienia sie z false na true". To tak nie dziala. Patrz raczej na to jako na zdarzenie po ktorym KeysDown[k] na pewno jest true.

Public OwnsMainMenu: boolean;

If true then in TGLWindow destructor MainMenu will be destroyed too (if not nil, od course). Usually this is something useful.

Public OnMenuCommand: TMenuCommandFunc;

Each time user will choose some menu item (let's name it MenuItem), we will call MenuItem.DoCommand. If this will return false then we will call EventMenuCommand (that will call OnMenuCommand).

Public UserData: Pointer;

UserData is a container for Your data associated with a given TGLWindow object. No code in this unit touches the value of this field.

Public KeysDown: TKeysBooleans;

You can check whether some key is pressed using array below. Note that this array is read-only from outside of this class ! Always KeysDown[K_None] = false.

Methods

Protected function MakeGLAreaContainer(GLArea: PGtkGLArea): PGtkWidget; virtual;

Implementation of MakeGLAreaContainer in this class simply returns Result := GLArea. When creating gtk window in Init, I'm 1st creating GLArea widget. Then I'm inserting MakeGLAreaContainer(GLArea) inside my window.

This means that you can override MakeGLAreaContainer if you want your window to contain something more than just OpenGL drawing area (and some menu, derived from MainMenu property). You can e.g. create in MakeGLAreaContainer some complex container, put there many various GTK widgets, connect your signals to them, and then put GLArea inside it. This way using this function you can extend window TGLWindow with whatever GTK GUI you like.

The only requirement is that GLArea widget must be placed somewhere inside widget that you return in this function.

Note: if you create here some widgets, remember to show them using gtk_widget_show.

Some notes about using this function: Using this function kills some part of portability of GLWindow unit, as this function exists only when GLWindow is implemented on top of GTK, so you will have to always use GLWindow implemented on top GTK on every OS, e.g. with Windows version of your program you will have to distribute GTK and GtkGLExt/GLArea for Windows. This is not really a problem, since GTK 2.x and GtkGLExt work excellent also under Windows (you know, GTK itself is a library that provides great portability). However this raises a question: what are advantages of using in such situation GLWindow unit (as opposed to just using directly Gtk and GtkGLExt API without any need for GLWindow unit): well, my advice is that if your program uses it's OpenGL area as it's central and crucial part (and using some GUI only to e.g. get some input from user about what to display in OpenGL area), than using GLWindow unit may be more straightforward as you have to write much less code in Gtk.

Example of using this can be found in ../examples/test_glwindow_gtk_mix

Public procedure EventResize; virtual;

EventXxx virtual methods ————————————————-

W sekcji private zdefiniowane sa procedury DoXxx ktore wykonuja niezalezna od implementacji GLWindow robote z opakowaniem zdarzen OnXxx. Oto jak to jest robione : najwazniejsza czescia tych procedur jest wywolanie odpowiedniej procedury EventXxx. Procedura EventXxx ma za zadanie z kolei wywolac odpowiednie OnXxx (o ile to jest zdefinoiwane, czyli Assigned(OnXxx)) i ew. OnXxxList jezeli zdarzenie ma swoja wersje listowa, jak OnInit czy OnClose. Wszystkie procedury OnXxx sa wywolywane przez try OnXxx except on BreakGLWinEvent do ; end albo cos rownowaznego. Czyli rzucenie w czasie OnXxx wyjatkiem BreakGLWinEvent spowoduje powrot do aktualnego EventXxx a sam wyjatek zostanie wyciszony (wylapany i zignorowany). W przypadku zdarzen listowych OnXxxList wyjatek zostanie wylapany w obrebie wywolania pojedynczego elementu listy, tzn. BreakGLWinEvent spowoduje przejscie do wykonywania nastepnej na liscie OnXxxList precedury.

Pytanie : po co nam ten dodatkowy stopien do zejscia, tzn. procedury EventXxx ? Mianowicie, procedury EventXxx sa wirtualne. W zwiazku z tym pozwalaja one na konstruowanie uzytecznych podklas klasy TGLWindow.

Przy okazji procedury te sa nie w protected ale w public aby umozliwic wygodne wywolywanie ich spoza klasy TGLWindow, np. gdy chcesz recznie spowodowac OnResize na jakims window wygodniej jest wywolac glwin.EventResize niz if Assigned(glwin.OnResize) then glwin.OnResize(glwin); albo nawet if Assigned(glwin.OnResize) then try glwin.OnResize(glwin) except on BreakGLWinevent do ; end

Pamietaj przy tym ze przed kazdym EventXxx musi byc wywolane MakeCurrent ! (DoXxx robia to automatycznie, podobnie jak pare wewnetrznych rzeczy).

Public procedure EventInit; virtual;
 
Public procedure EventClose; virtual;
 
Public function EventCloseQuery: boolean; virtual;

EventCloseQuery ma zwrocic true aby DoCloseQuery zrobilo Close

Public procedure EventDraw; virtual;
 
Public procedure EventBeforeDraw; virtual;
 
Public procedure EventKeyDown(Key: TKey; c: char); virtual;
 
Public procedure EventKeyUp(key: TKey); virtual;
 
Public procedure EventMouseMove(newX, newY: integer); virtual;
 
Public procedure EventMouseDown(btn: TMouseButton); virtual;
 
Public procedure EventMouseUp(btn: TMouseButton); virtual;
 
Public procedure EventIdle; virtual;

UWAGA : jezeli pokrywasz EventIdle/Timer musisz prawdopodobnie takze pokryc funkcje AllowsProcessMessageSuspend – patrz do jej opisu.

Public procedure EventTimer; virtual;
 
Public procedure EventMenuCommand(Item: TMenuItem); virtual;
 
Public function AllowsProcessMessageSuspend: boolean; virtual;

w glwm.ProcessMessage wykonywana jest bardzo wazna rzecz zaoszczedzajaca cykle procesora : program ktory nie ma zadnego idle ani timer zarejestrowanego ani nie zostalo wywolane glwm.Quit ma prawo "zawisnac" na oczekiwanie message'a od winsystemu.

Aby takie cos dzialalo musi byc sposob aby uzyskac od okna informacje czy wykonuje ono cos w EventIdle / EventTimer. (no bo jezeli tak to nie wolno nam robic takiego zawieszania sie). W bazowej klasie TGLWindow EventIdle / EventTimer wywoluja tylko OnIdle / OnTimer, w zwiazku z czym ta funkcja zwraca not (Assigned(OnIdle) or Assigned(OnTimer)) ale to sie moze zmienic jesli pokryjesz EventIdle/ Timer i dorobisz tam jakas funkcjonalnosc.

Public procedure SetMousePosition(const NewMouseX, NewMouseY: Integer);

This instructs window manager to place mouse at NewMouseX and NewMouseY position. NewMouseX and NewMouseY are specified just like MouseX and MouseY properties are given, so they are relative to OpenGL area, and 0,0 is upper-top corner. Note that the resulting position may be different than MouseX and MouseY, e.g. if part of the window is offscreen then window manager will probably refuse to move cursor offscreen.

This *may* generate normal OnMouseMove event, just as if the user moved the mouse. But it's also allowed to not do this.

Use this only when window is not closed.

Public procedure Init;

Initialize window (create window with GL context, show window).

Call to Init is ignored if not Closed., i.e. if window is already inited.

Raises EGLContextNotPossible if it's not possible to obtain OpenGL context with specified attributes. For example, maybe you set (Depth|Stencil|Accum)BufferBits properties to too high values. It's guaranteed that even when EGLContextNotPossible was raised, the window remains in correct (Closed) state, so you can try to lower some requirements and call init once again. For example:

  Shadows := true;
  Glw.StencilBufferBits := 8;
  try
    Glw.Init;
  except
    on EGLContextNotPossible do
    begin
      Shadows := false;
      Glw.StencilBufferBits := 0;
      { try to init once again, this time without requesting stencil buffer }
      Glw.Init;
    end;
  end;

Exceptions raised
EGLContextNotPossible
If it's not possible to obtain OpenGL context with specified attributes.
Public procedure Close(QuitWhenLastWindowClosed: boolean = true);

Close window.

  • Calls EventClose (and OnClose).

  • Hides window, destroys it.

  • if this was the only open TGLWindow window and QuitWhenLastWindowClosed = true then this calls glwm.Quit.

Note that often there's no need to call Close explicitly in your program, because in destructor of this object we call Close, to be sure that window is closed.

TODO: zrobic param boolean CloseFromDestroyQuitWhenLastWindowClosed ? As for now Close from destructor is called always with QuitWhenLastWindowClosed = true.

Call to Close is ignored if window is already Closed.

Public procedure PostRedisplay;

PostRedisplay says that contents of OpenGL area of this window must be redrawn. At the nearest free time (e.g. when events queue is empty in case of GLWINDOW_XLIB and GLWINDOW_WINAPI) we will call EventBeforeDraw, EventDraw (that call OnBeforeDraw, OnDraw) (and we will flush gl commands and swap buffers and do other things like that; see private method DoDraw for more precise description).

Note: if window is Closed then PostRedisplay is acceptable NOOP.

Public procedure FlushRedisplay;

FlushRedisplay mowi : jezeli zawartosc tego okna powinna zostac przemalowana (tzn. jest zgloszone i niezrealizowane PostRedisplay na tym oknie, przy czym pamietaj ze PostRedisplay moze byc takze zglaszane do okienka GLWindow przez WindowManagera) to wywolaj OnDraw TERAZ. To znaczy TERAZ - zanim FlushRedisplay powroci bedzie juz wykonane przemalowanie (o ile tylko bylo potrzebne).

Nie powinienes zbyt czesto potrzebowac tej funkcji. Psuje ona cala optymalizacje wyswietlania ktora stara sie wykonac wyswietlanie jak najpozniej wykonujac najpierw wszystko inne. Generalnie, napisanie kodu w rodzaju begin PostRedisplay; FlushRedisplay end powoduje natychmiastowe i bezwarunkowe odswiezenie ekranu. Kompletny nonsens, nie do takich celow pisalem ten unit.

Ale jest jeden wazny moment kiedy naprawde powinienes wywolac ta funkcje : kiedy zamierzasz zrobic zrzut wlasnego ekranu (np. przez glReadPixels). Zalezy ci wtedy zeby ekran przedstawial rzeczywiscie to co powinien - - i nie chcesz byc zalezny od tego czy ostatnie PostRedisplay zdazylo czy tez nie zdazylo sie wywolac. Przed kazdym glReadPixels i generalnie przed innymi operacjami odczytujacymi zawartosc buforow OpenGLa powinienes sie upewnic ze sa one odswiezone wywolujac FlushRedisplay;

Pamietaj ze FlushRedisplay moze wywolac EventDraw (OnDraw). Wiec lepiej zebys zadbal zeby wywolywane OnDraw dzialalo dobrze w momencie wywolania FlushRedisplay.

Public procedure MakeCurrent;

Each GLWindow has it's own OpenGL context. Before each window callback the calling window is guaranteed to be the current one - but sometimes you may need to manually set openGL context to a particular window. Note : Init of a window sets this window implicitly to be the current one !

Public procedure SaveScreen(const fname: string); overload;

The intention is to do: FlushRedisplay + KambiGLUtils.SaveScreenXxx_noflush(..,GL_FRONT) However: see comments at KambiGLUtils.SaveScreenXxx_noflush for some warnings about saving from front buffer. In short : you really do not want to save contents of front buffer. So when DoubleBuffer = true, these functions actually do something more reliable: EventDraw + KambiGLUtils.SaveScreenXxx_noflush(..,GL_BACK). (when DoubleBuffer = false no such workaround is possible so just be prepared for some nasty visual effects, as described in comments at KambiGLUtils.SaveScreenXxx_noflush).

If you *intent* to use something other than GL_FRONT, or simply have some more advanced needs than just "save current screen", use KambiGLUtils.SaveScreenXxx_noflush instead of these.

Public function SaveScreen: TRGBImage; overload;
 
Public function SaveScreen( const xpos, ypos, SavedAreaWidth, SavedAreaHeight: integer): TRGBImage; overload;
 
Public function SaveScreenToDisplayList: TGLuint; overload;
 
Public function SaveScreenToDisplayList(const xpos, ypos, SavedAreaWidth, SavedAreaHeight: integer): TGLuint; overload;
 
Public procedure SaveScreenDialog(ProposedFileName: string);

This asks user where to save the file (using FileDialog, as default filename taking ProposedFname), if user accepts calls glwin.SaveScreen(user-chosen-file-name);

Public function GetCallbacksState: TGLWindowCallbacks;

Methods for simply saving and restoring value of all OnXxx callbacks (with the exception of OnInit, OnInitList and OnClose, OnCloseList).

See also
DefaultCallbacksState
Public procedure SetCallbacksState(const Callbacks: TGLWindowCallbacks);
 
Public constructor Create;
 
Public destructor Destroy; override;
 
Public function ModifiersDown: TModifierKeys;

The same thing as Keys.ModifiersDown(KeysDown).

Public function FpsFrameTime: Double;

Fps _ Frame/RealTime: ile razy na sekunde aplikacja generuje ramke. Mierza one czas troche inaczej : RealTime zwraca prawde, tzn. liczy ile ramek na sekunde wyswietla twoja aplikacja. FrameTime robi w pewnym sensie oszustwo : za czas jaki uplynal uznaje czas spedzony tylko na renderowaniu ramek. Tzn. ze jesli FrameTime mowi np. "100 ramek na sek" to znaczy ze gdyby aplikacja spedzala caly czas na generowaniu ramek (tzn. na DrawGL + glFlush / swap buffers robione w ProcessGLWinMessage) to robilaby ich 100 na sekunde. FrameTime nie uwzglednia faktu ze aplikacja moze spedzac duzo czasu takze na innych zajeciach, w OnIdle na przyklad. Przy okazji : widac stad ze na wartosc FramesPerSec duzy wplyw ma to co rzeczywiscie robisz w DrawGL. Wszystko co robisz w DrawGL FrameTime uznaje za "renderowanie ramki".

Public function FpsRealTime: Double;
 
Public procedure FpsToCaption(const WindowTitle: string);

wypisz ladnie FramesPerSec Frame/Real Time na GLWindow.Caption

Public procedure FpsReset;

Aby zresetowac timer mozna wywolac FpsActive(false) i potem (true). Mozna tez uzyc ponizszej procedury FpsReset ktora robi to troche szybciej. Ale zazwyczaj nie ma w ogole potrzeby resetowac "recznie" timera bo co FpsSecondsToAutoReset timer sam sie zresetuje. Dla bezpieczenstwa - zmieniaj wartosc zmiennej FpsSecondsToAutoReset tylko gdy timer nie jest active.

Public procedure InitLoop; overload;

———————————————————————— gotowe funkcje ktore realizuja "uproszczony scenariusz", same inicjuja typowe wartosci, wywoluja Init a potem glwm.Loop;

Public procedure InitLoop(const ACaption: string; AOnDraw: TDrawFunc); overload;
 
Public procedure ParseParameters(); overload;

Parse some parameters from Parameters[1]..Parameters[Parameters.High]. Delete processed parameters from Parameters. Arguments to this proc tell which options are allowed:

poGeometry : allowed options are –fullscreen (ustawia Fullscreen := true) –geometry followed by param WIDTHxHEIGHTsXOFFsYOFF gdzie WIDTH, HEIGHT sa liczbami calkowitymi, XOFF, YOFF sa liczbami calkowitymi z opcjonalnym znakiem. (ustawia Fullscreen := false i Width, Height, Left, Top odpowednio - patrz 'man X' po opis co mozna wyrazic parametrem -geometry)

poScreenGeometry –fullscreen-custom WIDTHxHEIGHT (ustawia Fullscreen = true, VideoResize := true, VideResizeWidth/Height inicjuje i robi VideoChange)

poDisplay –display (set Glwm.XDisplayName)

Multiple options of the same kind are allowed, for example two options –fullscreen –geometry 100x100+0+0 are allowed. Each of them will have appropriate effect - in the above example, –fullscreen param will be useless (it will be "overriden" by –geometry param that comes later). This is to allow flexible calling my programs from shell scripts etc.

Jezeli parametry sa zle (np. poGeometry in AllowedOptions i zly format parametru za –geometry lub brak parametru za –geometry) -> -> wyjatek EInvalidParams.

Wersja 2-argumentowa zwraca jakie grupy parametrow zostaly odczytane i zinterpretowane. Np. jezeli poGeometry in SpecifiedOptions to wiesz ze user podal window size i position i nie powinnismy juz sami tego ustawiac. Chociaz zazwyczaj wystarczy po prostu ustawic w programie Width/Height/Left/Top i potem wywolac ParseParameters i wtedy juz nie trzeba przejmowac sie czy poGeometry bylo czy nie bylo w SpecifiedOptions.

Public procedure ParseParameters(const AllowedOptions: TGLWindowParseOptions); overload;
 
Public procedure ParseParameters(const AllowedOptions: TGLWindowParseOptions; out SpecifiedOptions: TGLWindowParseOptions); overload;
 
Public class function ParseParametersHelp( const AllowedOptions: TGLWindowParseOptions; AddHeader: boolean): string;

Returns help text for options in AllowedOptions. The idea is that if you call ParseParameters(AllowedOptions) in your program then you should also show your users somwhere (e.g. in response to "–help" option) the list of allowed options obtained by ParseParametersHelp(AllowedOptions) (i.e. with the same value of AllowedOptions).

Returned string may be multiline, but it does not contain the trailing newline (newline char after the last line).

Returned help text conforms my rules in file base/README.kambi_command_line_params

If AddHeader then it adds text 'Window options:' +nl at the beginning. This is just a small thing that allows you to comfortably use the output of this function as a whole paragraph (separated from the rest of your "–help" text by e.g. empty lines around).

Public function FileDialog(const Title: string; var FileName: string; OpenDialog: boolean): boolean;

Title is some dialog title. FileName specifies default filename (path and/or name, or '' if current dir is the default dir and there is no default filename). Note that if you have to specify only path in FileName you have to end this paths with PathDelim (otherwise '/tmp/blah' would not be clear: whether it's filename 'blah' in '/tmp/' dir or whether it's only dir '/tmp/blah/' ?).

Returns true and sets FileName accordingly if user chooses some filename and accepts it. Returns false if user cancels.

if OpenDialog: may try to force user to only enter existing (and readable) FileName. (It may be unable to force this from user, or it may fail, so you should watch for some exceptions when opening a file (as it always the case with opening files, anyway)). The intention is that you should be able to open FileName for reading (or reading and writing; anyway, file contents should exist).

if not OpenDialog: allows user to select a non-existent filename. Still, it may try to force ExtractFilePath(FileName) to be valid, i.e. user may be forced to choose only filenames with existing paths. (of course, here it may fail TOO. So you should not assume for sure that ExtractFilePath(FileName) is valid). Some warning to user may be shown if FileName already exists, like "are you sure you want to overwrite this file ?". The intention is that you should be able to open FileName for writing. This is the "Save File" dialog.

Those dialog boxes may allow user for some additional actions, e.g. to create some directories, rename some files etc.

Public function ColorDialog(var Color: TVector3Single): boolean;

Shows a dialog window allowing user to choose an RGB color. Initial value of Color specifies initial RGB values proposed to the user. If user accepts, returns true and sets Color accordingly, else returns false (and does not modify Color).

Properties

Public property BorderWidth: Cardinal read FBorderWidth write FBorderWidth default 0;

Change this only when Closed. This is the width of border of main GtkWindow that will be created in Init, set with gtk_container_set_border_width.

Public property Width: integer read FWidth write FWidth default GLWindowDefaultSize;

Left, Top, Width i Height caly czas okreslaja ClientRect - a wiec obszar na ktorym bedzie tworzony i uzywany kontekst OpenGLa. Nie uwzgledniaja ramek jakie WindowManager dodal do naszego okienka. And they don't take space taken by menu (if MainMenu <> nil) into account. So these are always dimensions of our 3d window — nothing more, nothing less.

min/maxWidth/Height i ResizeAllowed ustawiaja scisle ograniczenia na Width i Height ktore sa poprawiane zgodnie z tymi wlasciwosciami podczas wywolywania Init. PO wywolaniu Init (tzn. pomiedzy Init a Close) jest gwarantowane ze - minWidth<= Width<= maxWidth - minHeight<= Height<= maxHeight - Width i Height nie ulegna zmianie jezeli not ResizeAllowed <> raAllowed

Poniewaz WindowManager (WindowManager X-ow lub Windows) moze dosc swobodnie traktowac nasze wymagania min/maxWidth/Height i ResizeAllowed wiec jest niestety mozliwe ze rzeczywiste wymiary okienka beda sie roznic od zadanych w width, height - zauwazysz to zwlaszcza jezeli ustawiles istotne ograniczenia na min/maxWidth/Height lub jezeli ustawiles ResizeAllowed <> raAllowed. Wiec TGLWindow robi tak ze moze nieco przeklamywac wlasciwosci Width / Height - tak zeby nasze wlasciwoci Width/Height ZAWSZE spelnialy zadane ograniczenia, nawet jezeli w rezultacie moga sie one czasem roznic od rzeczywistych rozmiarow okienka.

Aplikacje polegajace na tym na poczatku (po zainicjowaniu gl contextu) glViewport jest ustawiony na wymiary okienka moga byc spokojne : wprawdzie rzeczywiste okienko moze nie miec rozmiarow Width/Height, poczatkowe glViewport bedzie na pewno zgodne z NASZYMI Width/Height.

GLWindowDefaultSize will be treated specifically: at Init, will be replaced with some comfortable size slightly smaller than screen size.

Public property Height: integer read FHeight write FHeight default GLWindowDefaultSize;
 
Public property Left: integer read FLeft write FLeft;

jezeli Left / Top rowne sa GLWindowPositionCenter w momencie wywolania Init to zostana zainicjowane tak zeby okienko bylo na srodku ekranu.

Public property Top : integer read FTop write FTop;

= GLWindowPositionCenter

Public property FullScreen: boolean read FFullScreen write FFullScreen;

= GLWindowPositionCenter

Public property DoubleBuffer: boolean read FDoubleBuffer write FDoubleBuffer default true;

DoubleBuffer to swapBuffers bedzie robione automatycznie po kazdym repaincie. jesli false - bedzie robione glFlush.

Public property ColorBits: integer read FColorBits write FColorBits default 0;

Warning: this symbol is deprecated.

ColorBits : sprobuje ustawic takie bits per pixel tylko dla danego okna. Jesli ColorBits = 0 w czasie Init to uzyje glwm.VideoColorBits (chociaz one tez moga byc = 0; wtedy wezmie defaultowe ColorBits jakie da nam Windows). Tak czy siak, po zakonczeniu Init ColorBits powiedza nam jakie ColorBits otrzymalismy. Aby naprawde zmienic ColorBits z duza szansa uzywaj raczej Glwm.VideoColorBits i glwm.VideoChange.

TODO: uzywanie tej wlasciwosci jest deprecated. Jest ona non-cross-platform, interfejs nie czyni zadnych gwarancji ze rzeczywiscie dostaniemy wymagane ColorBits, ponadto nie powinnismy zmieniac ColorBits po Init - po wyjasnienie dlaczego patrz komentarze do StencilbufferBits.

Public property Cursor: TGLWindowCursor read FCursor write SetCursor default gcDefault;

Sets mouse cursor appearance over this window. See TGLWindowCursor for a list of possible values and their meanings.

TODO: for now, gcCustom is not handled anywhere.

Public property CustomCursor: TAlphaImage read FCustomCursor write SetCustomCursor;

Image for cursor, used only when Cursor = gcCustom. We will try hard to use any cursor image as appropriate, but on some platforms cursor size may be limited (16 x 16 seems standard for GTK) and cursor may be forced to monochrome.

Note that you still own the TAlphaImage instance passed here — you're responsible for freeing it etc. If this is Nil, and Cursor = gcCustom, then it will be treated like Cursor = gcDefault. (I don't raise error in such case, as that would make changing both Cursor and CustomCursor values unnecessarily tricky for the programmer.)

TODO: for now, this is not implemented. Cursor ignores gcCustom value, under every GLWindow implementation... sorry, CustomCursor is only a plan.

Public property ResizeAllowed: TResizeAllowed read FResizeAllowed write FResizeAllowed;

Naklada ograniczenia na to kiedy i jak Width i Height moga sie zmienic. = raNotAllowed oznacza ze Width i Height nie moga sie zmienic CHYBA ze po to zeby dostosowac sie do min/maxWidth/Height. Tak ostre ograniczenia moga nawet spowodowac ze przy probie Init okienka z atrybutem Fullscreen = true flaga Fullscreen moze zostac wylaczone na true. Ale masz PEWNOSC ze Width i Height zawsze beda rowne zadanym, o ile tylko beda w granicach min/maxWidth/Height. = raOnlyAtInit oznacza ze rozmiary okienka moga zostac zainicjowane na inne niz podane jezeli np. WindowManager ma obiekcje co do zadanych przez nas rozmiarow okienka albo jezeli chcesz miac Fullscreen i ScreenWidth/H sa rozne od Width/Height. W tych przypadkach, i byc moze takze w innych podobnych rozmiary Width/Height jakie dostanie okienko beda rozne od zadanych Width/Height. Ale masz PEWNOSC ze po wywolaniu pierwszego callbacka (czyli EventInit (OnInit), tuz przed pierwszym EventResize (OnResize)) Width/Height juz beda stale, dopoki okienko bedzie not Closed. = raAllowed jest domyslne i pozwala na najwieksza swobode WindowManagerowi i userowi : okienko moze byc zresizowane w dowolnym momencie. Oznacza to ze nie tylko Width/Height jakie dostaniesz w pierwszych OnInit i OnResize moga byc inne niz te ktorych zazadales ale takze w trakcie dzialania programu rozmiary okienka moga sie zmieniac. Powinienes byc na to przygotowany obslugujac zdarzenie OnResize (ktore w zasadzie jest zupelnie zbedne w pozostalych przypadkach, gdy ResizeAllowed<> raAllowed), zapewne ustawiajac w nim odpowiednie glViewport i uaktualniajac macierz projection. ResizeAllowed <> raAllowed oznacza ze do okna bedzie wyslane tylko raz OnResize - na poczatku, pod koniec wykonywania Init (chociaz w zasadzie i tak bedziesz mogl je zignorowac i obsluzyc wszystko w OnInit; chociaz moze czasem bedziesz jednak chcial zapisac ustawianie glViewport i projection w OnResize, dla porzadku). Poniewaz pierwsze glViewport (przed wywolaniem pierwszych callbackow EventInit (OnInit) i EventResize (OnResize) w Init) jest wykonane automatycznie to w rezultacie programy majace ResizeAllowed <> raAllowed nie musza sie nigdy martwic o robienie kiedykolwiek glViewport.

Public property OnInit: TGLWindowFunc read FOnInit write FOnInit;

Jest GWARANTOWANE ze dla kazdego nowootwartego okna NAJPIERW zostanie wyslany EventInit (OnInit) and THEN EventResize (OnResize).

This is a sensible and consequential approach – EventInit (OnInit) is always the first executed callback and EventClose (OnClose) is always last. Some examples (like gl_win_events.pasprogram) confirm that this is really useful.

One more thing: in EventInit (OnInit) you already have valid Width/Height values, i.e. those values were already adjusted if ResizeAllowed <> raNotAllowed. If ResizeAllowed = raNotAllowed then Width and Height are constant, so this is obvious.

Public property DepthBufferBits: Cardinal read FDepthBufferBits write FDepthBufferBits default DefaultDepthBufferBits;

Required depth buffer precision. Zero means that we don't need depth buffer at all. We may get depth buffer with more precision than requested (we may even get depth buffer when we set DepthBufferBits = 0), this all depends on graphic card.

Default value is 16 (DefaultDepthBufferBits), which is a reasonable default for 3D programs that want to work with depth test enabled.

Design notes: One may ask why default value is not 0 ?

  1. Most programs using OpenGL use depth testing, so many programs would have to call something like Glw.DepthBufferBits := 16.

  2. Often graphic cards / window systems / OSes give you an OpenGL context with depth buffer even if you don't need depth buffer. I don't say that it's bad. But it makes very easy to forget about doing DepthBufferBits := something-non-zero;. If you're writing 3d program and sitting on some system that always gives you depth buffer (even if DepthBufferBits = 0) then it may happen that you forget to write in your program

      glw.DepthBufferBits := 16;

    And while on your system everything will work, you will receive errors on other systems because you forgot to request a depth buffer.

Of course, if you are writing a program that does not need depth buffer you should set glw.DepthBufferBits := 0. The only advantage of having default DepthBufferBits = 16 is that if you forget to set Glw.DepthBufferBits := 0 your programs will still work (most graphic cards will give you some depth buffer anyway). They will just use more resources than they should.

Public property StencilBufferBits: Cardinal read FStencilBufferBits write FStencilBufferBits default 0;

Required stencil buffer precision, zero means that stencil buffer is not needed.

Just like with other XxxBufferBits property, we may get more bits than we requested. But we will never get less — if window system will not be able to provide GL context with requested number of bits, Init will raise an error.

Public property MultiSampling: Cardinal read FMultiSampling write FMultiSampling default 1;

How many samples are required for multisampling. 1 means that no multisampling is required. Values larger than 1 means that we require OpenGL context with multisampling capabilities (GLX_ARB_multisample for glX on Unix or WGL_ARB_multisample for wgl on Windows). MultiSampling says how many samples per pixel should be done (try typical 2 or 4 values).

So the only thing remaining for your program to make anti-aliasing working is to use core OpenGL extension GL_ARB_multisample: [http://opengl.org/registry/specs/ARB/multisample.txt]. In the usual case, this means simply to call

  if GL_ARB_multisample then glEnable(GL_MULTISAMPLE_ARB); 

and

  if GL_ARB_multisample then glDisable(GL_MULTISAMPLE_ARB); 

Just like with other XxxBufferBits property, we may get more samples than we requested (e.g. if you request 3, you will most probably get 4...). But we will never get less — if window system will not be able to provide GL context with requested number of bits, Init will raise an error.

Public property AlphaBits: Cardinal read FAlphaBits write FAlphaBits default 0;

Required number of bits in alpha channel of color buffer. Zero means that alpha channel is not needed.

Just like with other XxxBufferBits property, we may get more bits than we requested. But we will never get less — if window system will not be able to provide GL context with requested number of bits, Init will raise an error.

It's undefined how I'll treat this variable when indexed color mode will be possible in TGLWindow.

Public property Caption: string read FCaption write SetCaption;

———————————————————————– rzeczy ktore mozesz inicjowac przed wywolaniem Init ale mozesz tez nimi swobodnie manipulowac pozniej.

Public property OnDraw: TDrawFunc read FOnDraw write FOnDraw;

This is probably the most important callback. You should redraw your window here.

It will be called when your window contents must be redrawn, e.g. after creating a window, after resizing a window, after uncovering the window etc. You can also request that this callback should be called in a short time by PostRedisplay.

Note: calling PostRedisplay while in EventDraw (OnDraw) is NOT ignored. It means that in a short time next EventDraw (OnDraw) will be called.

Public property OnBeforeDraw: TDrawFunc read FOnBeforeDraw write FOnBeforeDraw;

OnBeforeDraw will be always called right before OnDraw (more specifically, EventBeforDraw will be always called right before EventDraw). So those two functions, EventBeforeDraw and EventDraw, will be always called sequentially as a pair. So what is the use of BeforeDraw ?

Only one thing differs OnDraw and OnBeforeDraw: time spent in OnBeforeDraw (more specifically, in EventBeforeDraw) is NOT counted as "frame time" by FpsFrameTime. This is useful when you have something that needs to be done from time to time right before OnDraw and that is very time-consuming. It such cases it is not desirable to put such time-consuming task inside OnDraw because this would cause a sudden big change in FpsFrameTime value (and FpsCompSpeed). So you can avoid this by putting this in OnBeforeDraw. Of course, using OnBeforeDraw also means that the program will not always be time-based. By using OnBeforeDraw instead of OnDraw you're breaking the rules that are designed to make time-based program (a program that adjusts itself to the speed of computer it is on). But this is sometimes desirable when you have something that you want to do really seldom (e.g. only when user presses some special key) (but that is also time-consuming).

E.g. view3dscene uses Scene.Render. From time to time user presses a key like "g" or "m" that force the whole Scene to be regenerated. This means that a call to Scene.Render inside OnDraw takes sometimes much more time than it usually does. This gives some unpleasant effect because when the user views the scene using "Examine" mode then after pressing "g" FpsCompSpeed is (for only one short moment !) very big. And this means that after pressing "g" user sees 1. first, it takes some time to regenerate the model 2. second, after regenerating the model there is a sudden jump in the amount the object is rotated (because FpsCompSpeed is big). And the second thing is bad. It can avoided by putting Scene.PrepareRender(true) after OnKeyDown, OnMouseDown etc. But the simplest (and more elegant) way is to put Scene.PrepareRender(true) only in OnBeforeDraw.

Public property OnResize: TGLWindowFunc read FOnResize write FOnResize;

OnResize - wywolywane zawsze gdy okienko bedzie zresizowane, tzn. gdy zmienia sie width i/lub height tego obiektu. Ponadto, jest gwarantowane ze OnResize bedzie wywolane zawsze w czasie Init tuz po EventInit (OnInit). Przed wywolaniem OnResize width i height okienka sa juz uaktualnione, jest zrobione MakeCurrent. Nie ma problemu jesli OnResize = nil, chociaz zazwyczaj jest to dobre miejsce na ustawienie projection matrix.

Kiedys bylo automatyczne glViewport przed OnResize - teraz NIE JEST, patrz old\GLWindow_why_not_Viewport_in_OnResize.txt po wyjasnienie dlaczego, w skrocie - zazwyczaj glViewport powinnno isc w parze ze zmiana projection i rozbijanie tego latwo doprowadzi nas do bledow w specjalnych przypadkach.

I jeszcze jest gwarantowane ze zdarzenie OnResize zajdzie pierwszy raz nawet gdy ResizeAllowed <> raAllowed.

2D OpenGL programs may want to register here simple Resize2D.

Public property OnClose: TGLWindowFunc read FOnClose write FOnClose;

jesli assigned, to bedzie wywolywane OnClose w momencie zamykania okna - czyli robienia Close. Ta procedura jest ostatnia szansa na zrobienie czegos zanim kontekst OpenGL'a zostanie zniszczony i jest lustrzanym odbiciem OnInit; dobrym miejscem na niszczenie tego co stworzylo OnInit jest wlasnie OnClose (np. tutaj niszcz fonty OpenGL'a ktore potrzebuja kontekstu OpenGL'a zeby ladnie wyczyscic po sobie zasosby OSa)

Public property OnKeyUp: TKeyFunc read FOnKeyUp write FOnKeyUp;

OnKeyUp(Self, Key): Called when user releases a pressed key. I.e. it's called right after KeysDown[Key] changed from true to false.

Key is never K_None.

Note: I had at some time an idea to add "c: char" parameter to OnKeyUp, analogous to OnKeyDown. But I decided that it would be rather counterintuitive: what will be the relation between Key and c ? In OnKeyDown, c is said to be determined by Key + modifiers (ctrl, shift etc.) state + some OS-specific conversions. In OnKeyUp, what modifiers state (and OS configuration) should we use ? Same one as in last OnKeyDown with such Key ? But this means using not current information about modifiers state and OS configuration. So it's rather useless, and troublesome to implement. Current ? But this means that OnKeyDown and OnKeyUp does not make "correspoing pairs of events". E.g. you press K_Shift, then