mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-28 22:33:41 +02:00
1020 lines
30 KiB
ObjectPascal
1020 lines
30 KiB
ObjectPascal
{
|
|
*****************************************************************************
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
}
|
|
unit GLGtkGlxContext;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$LinkLib GL}
|
|
{$PACKRECORDS C}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, ctypes, LCLProc, LCLType, X, XUtil, XLib, gl,
|
|
InterfaceBase,
|
|
glx,
|
|
WSLCLClasses,
|
|
{$IFDEF LCLGTK2}
|
|
LMessages, Gtk2Def, gdk2x, glib2, gdk2, gtk2, Gtk2Int,
|
|
{$ENDIF}
|
|
{$IFDEF LCLGTK}
|
|
glib, gdk, gtk, GtkInt,
|
|
{$ENDIF}
|
|
Controls;
|
|
|
|
type
|
|
TGLBool = longbool;
|
|
const
|
|
GLXTrue:longbool = true;
|
|
GLXFalse:longbool = false;
|
|
|
|
type
|
|
TGdkGLContext = record end;
|
|
PGdkGLContext = ^TGdkGLContext;
|
|
|
|
// GLX_EXT_visual_info extension
|
|
|
|
function gdk_gl_query: boolean;
|
|
function gdk_gl_choose_visual(attrlist: Plongint): PGdkVisual;
|
|
function gdk_gl_get_config(visual: PGdkVisual; attrib: longint): longint;
|
|
function gdk_gl_context_new(visual: PGdkVisual; attrlist: PlongInt): PGdkGLContext;
|
|
function gdk_gl_context_share_new(visual: PGdkVisual; sharelist: PGdkGLContext;
|
|
direct: TGLBool; attrlist: plongint): PGdkGLContext;
|
|
function gdk_gl_context_attrlist_share_new(attrlist: Plongint;
|
|
sharelist: PGdkGLContext; direct: TGLBool): PGdkGLContext;
|
|
function gdk_gl_context_ref(context: PGdkGLContext): PGdkGLContext;
|
|
function gdk_gl_context_unref(context:PGdkGLContext): PGdkGLContext;
|
|
function gdk_gl_make_current(drawable: PGdkDrawable;
|
|
context: PGdkGLContext): boolean;
|
|
procedure gdk_gl_swap_buffers(drawable: PGdkDrawable);
|
|
procedure gdk_gl_wait_gdk;
|
|
procedure gdk_gl_wait_gl;
|
|
|
|
{ glpixmap stuff }
|
|
|
|
type
|
|
TGdkGLPixmap = record end;
|
|
PGdkGLPixmap = ^TGdkGLPixmap;
|
|
TGLXContext = pointer;
|
|
|
|
|
|
// gtkglarea
|
|
|
|
type
|
|
TGtkGlAreaMakeCurrentType = boolean;
|
|
|
|
PGtkGLArea = ^TGtkGLArea;
|
|
TGtkGLArea = record
|
|
darea: TGtkDrawingArea;
|
|
glcontext: PGdkGLContext;
|
|
end;
|
|
|
|
PGtkGLAreaClass = ^TGtkGLAreaClass;
|
|
TGtkGLAreaClass = record
|
|
parent_class: TGtkDrawingAreaClass;
|
|
end;
|
|
|
|
TContextAttribs = record
|
|
AttributeList: PLongint;
|
|
MajorVersion: Cardinal;
|
|
MinorVersion: Cardinal;
|
|
MultiSampling: Cardinal;
|
|
ContextFlags: Cardinal;
|
|
end;
|
|
|
|
function GTK_TYPE_GL_AREA: TGtkType;
|
|
function GTK_GL_AREA(obj: Pointer): PGtkGLArea;
|
|
function GTK_GL_AREA_CLASS(klass: Pointer): PGtkGLAreaClass;
|
|
function GTK_IS_GL_AREA(obj: Pointer): Boolean;
|
|
function GTK_IS_GL_AREA_CLASS(klass: Pointer): Boolean;
|
|
|
|
function gtk_gl_area_get_type: TGtkType;
|
|
function gtk_gl_area_new(Attribs: TContextAttribs): PGtkWidget;
|
|
function gtk_gl_area_share_new(Attribs: TContextAttribs; share: PGtkGLArea): PGtkWidget;
|
|
function gtk_gl_area_share_new_usefpglx(Attribs: TContextAttribs; share: PGtkGLArea): PGtkGLArea;
|
|
function gtk_gl_area_make_current(glarea: PGtkGLArea): boolean;
|
|
function gtk_gl_area_begingl(glarea: PGtkGLArea): boolean;
|
|
procedure gtk_gl_area_swap_buffers(gl_area: PGtkGLArea);
|
|
|
|
{$IFDEF lclgtk}
|
|
function gdk_x11_get_default_xdisplay:PDisplay;cdecl;external;
|
|
function gdk_x11_get_default_screen:gint;cdecl;external;
|
|
{$ENDIF}
|
|
|
|
procedure LOpenGLViewport({%H-}Handle: HWND; Left, Top, Width, Height: integer);
|
|
procedure LOpenGLSwapBuffers(Handle: HWND);
|
|
function LOpenGLMakeCurrent(Handle: HWND): boolean;
|
|
function LOpenGLReleaseContext({%H-}Handle: HWND): boolean;
|
|
function LOpenGLCreateContext(AWinControl: TWinControl;
|
|
WSPrivate: TWSPrivateClass; SharedControl: TWinControl;
|
|
DoubleBuffered, RGBA, DebugContext: boolean;
|
|
const RedBits, GreenBits, BlueBits, MajorVersion, MinorVersion,
|
|
MultiSampling, AlphaBits, DepthBits, StencilBits, AUXBuffers: Cardinal;
|
|
const AParams: TCreateParams): HWND;
|
|
procedure LOpenGLDestroyContextInfo(AWinControl: TWinControl);
|
|
|
|
{ Create GLX attributes list suitable for glXChooseVisual or glXChooseFBConfig. }
|
|
function CreateOpenGLContextAttrList(DoubleBuffered: boolean;
|
|
RGBA: boolean;
|
|
const RedBits, GreenBits, BlueBits,
|
|
AlphaBits, DepthBits, StencilBits, AUXBuffers: Cardinal): PInteger;
|
|
|
|
implementation
|
|
|
|
|
|
var
|
|
gl_area_type: TGtkType = 0;
|
|
parent_class: Pointer = nil;
|
|
|
|
type
|
|
TGdkGLContextPrivate = record
|
|
xdisplay: PDisplay;
|
|
glxcontext: TGLXContext;
|
|
ref_count: gint;
|
|
end;
|
|
PGdkGLContextPrivate = ^TGdkGLContextPrivate;
|
|
|
|
type
|
|
//PGLXPixmap = ^GLXPixmap;
|
|
GLXPixmap = {%H-}TXID;
|
|
|
|
//PGLXDrawable = ^GLXDrawable;
|
|
GLXDrawable = {%H-}TXID;
|
|
|
|
procedure g_return_if_fail(b: boolean; const Msg: string);
|
|
begin
|
|
if not b then raise Exception.Create(Msg);
|
|
end;
|
|
|
|
procedure g_return_if_fail(b: boolean);
|
|
begin
|
|
g_return_if_fail(b,'');
|
|
end;
|
|
|
|
function DefaultScreen(ADisplay: PDisplay): longint;
|
|
begin
|
|
Result:=XDefaultScreen(ADisplay);
|
|
end;
|
|
|
|
function g_new(BaseSize, Count: integer): Pointer;
|
|
begin
|
|
Result:=g_malloc(BaseSize*Count);
|
|
end;
|
|
|
|
function GetDefaultXDisplay: PDisplay;
|
|
begin
|
|
Result:=GDK_DISPLAY;
|
|
end;
|
|
|
|
{$IFDEF LCLGtk2}
|
|
function GdkVisualAsString(Visual: PGdkVisual): string;
|
|
begin
|
|
if Visual=nil then begin
|
|
Result:='nil';
|
|
end else begin
|
|
with Visual^ do begin
|
|
Result:=''
|
|
//parent_instance : TGObject;
|
|
+' TheType='+dbgs(ord(TheType))
|
|
+' depth='+dbgs(depth)
|
|
+' byte_order='+dbgs(ord(byte_order))
|
|
+' colormap_size='+dbgs(colormap_size)
|
|
+' bits_per_rgb='+dbgs(bits_per_rgb)
|
|
+' red_mask='+hexstr(red_mask,8)
|
|
+' red_shift='+dbgs(red_shift)
|
|
+' red_prec='+dbgs(red_prec)
|
|
+' green_mask='+hexstr(green_mask,8)
|
|
+' green_shift='+dbgs(green_shift)
|
|
+' green_prec='+dbgs(green_prec)
|
|
+' blue_mask='+hexstr(blue_mask,8)
|
|
+' blue_shift='+dbgs(blue_shift)
|
|
+' blue_prec='+dbgs(blue_prec)
|
|
//screen : PGdkScreen;
|
|
+'';
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function XVisualAsString(AVisual: PVisual): string;
|
|
begin
|
|
if AVisual=nil then begin
|
|
Result:='nil';
|
|
end else begin
|
|
Result:=''
|
|
+' bits_per_rgb='+dbgs(AVisual^.bits_per_rgb)
|
|
+' red_mask='+hexstr(AVisual^.red_mask,8)
|
|
+' green_mask='+hexstr(AVisual^.green_mask,8)
|
|
+' blue_mask='+hexstr(AVisual^.blue_mask,8)
|
|
+' map_entries='+dbgs(AVisual^.map_entries)
|
|
+'';
|
|
end;
|
|
end;
|
|
|
|
function XDisplayAsString(ADisplay: PDisplay): string;
|
|
begin
|
|
if ADisplay=nil then begin
|
|
Result:='nil';
|
|
end else begin
|
|
Result:=''
|
|
+'';
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function get_xvisualinfo(visual: PGdkVisual): PXVisualInfo;
|
|
// IMPORTANT: remember to XFree returned XVisualInfo !!!
|
|
var
|
|
vinfo_template: TXVisualInfo;
|
|
dpy: PDisplay;
|
|
nitems_return: integer;
|
|
vi: PXVisualInfo;
|
|
begin
|
|
dpy := GetDefaultXDisplay;
|
|
{$IFDEF Lclgtk2}
|
|
DebugLn('get_xvisualinfo dpy=',XDisplayAsString(dpy));
|
|
DebugLn('get_xvisualinfo visual=',GdkVisualAsString(Visual));
|
|
RaiseGDBException('not implemented for gtk2');
|
|
{$ENDIF}
|
|
|
|
// 'GLX uses VisualInfo records because they uniquely identify
|
|
// a (VisualID,screen,depth) tuple.'
|
|
vinfo_template.bits_per_rgb:=0;
|
|
FillChar(vinfo_template,SizeOf(vinfo_template),0);
|
|
vinfo_template.visual := GDK_VISUAL_XVISUAL({$IFDEF LCLGTK}
|
|
PGdkVisualPrivate(visual)
|
|
{$ELSE}
|
|
visual
|
|
{$ENDIF});
|
|
vinfo_template.visualid := XVisualIDFromVisual(vinfo_template.visual);
|
|
vinfo_template.depth := PGdkVisualPrivate(visual)^.visual.depth;
|
|
vinfo_template.screen := DefaultScreen(GetDefaultXDisplay);
|
|
{$IFDEF LCLGTK2}
|
|
DebugLn('get_xvisualinfo vinfo_template.visual=',dbgs(vinfo_template.visual));
|
|
DebugLn('get_xvisualinfo vinfo_template.visual: ',XVisualAsString(vinfo_template.visual));
|
|
DebugLn('get_xvisualinfo vinfo_template.visualid=',dbgs(vinfo_template.visualid));
|
|
DebugLn('get_xvisualinfo vinfo_template.depth=',dbgs(vinfo_template.depth),' GetDefaultXDisplay=',dbgs(GetDefaultXDisplay));
|
|
DebugLn('get_xvisualinfo vinfo_template.screen=',dbgs(vinfo_template.screen));
|
|
{$ENDIF}
|
|
vi := XGetVisualInfo(dpy, VisualIDMask or VisualDepthMask or VisualScreenMask,
|
|
@vinfo_template, @nitems_return);
|
|
DebugLn('get_xvisualinfo nitems_return=',dbgs(nitems_return));
|
|
// visualinfo needs to be unique
|
|
if (vi=nil) then raise Exception.Create('get_xvisualinfo vi=nil');
|
|
if (nitems_return<>1) then raise Exception.Create('get_xvisualinfo nitems_return='+dbgs(nitems_return));
|
|
|
|
Result:=vi;
|
|
end;
|
|
|
|
procedure gtk_gl_area_destroy(obj: PGtkObject); cdecl;
|
|
var
|
|
gl_area: PGtkGLArea;
|
|
begin
|
|
g_return_if_fail (obj <>nil,'');
|
|
g_return_if_fail (GTK_IS_GL_AREA(obj),'');
|
|
|
|
gl_area := GTK_GL_AREA(obj);
|
|
if gl_area^.glcontext <> nil then // avoid double-free
|
|
gl_area^.glcontext := gdk_gl_context_unref(gl_area^.glcontext);
|
|
|
|
if Assigned(GTK_OBJECT_CLASS(parent_class)^.destroy) then
|
|
GTK_OBJECT_CLASS(parent_class)^.destroy(obj);
|
|
end;
|
|
|
|
procedure gtk_gl_area_class_init(klass: Pointer); cdecl;
|
|
var
|
|
object_class: PGtkObjectClass;
|
|
begin
|
|
parent_class := gtk_type_class(gtk_drawing_area_get_type());
|
|
g_return_if_fail(parent_class<>nil,'gtk_gl_area_class_init parent_class=nil');
|
|
object_class := PGtkObjectClass(klass);
|
|
g_return_if_fail(object_class<>nil,'gtk_gl_area_class_init object_class=nil');
|
|
|
|
object_class^.destroy := @gtk_gl_area_destroy;
|
|
end;
|
|
|
|
function gdk_gl_query: boolean;
|
|
var
|
|
errorb: Integer = 0;
|
|
event: Integer = 0;
|
|
begin
|
|
Result:=boolean(glXQueryExtension(GetDefaultXDisplay, errorb, event));
|
|
end;
|
|
|
|
function gdk_gl_choose_visual(attrlist: Plongint): PGdkVisual;
|
|
var
|
|
dpy: PDisplay;
|
|
vi: PXVisualInfo;
|
|
visual: PGdkVisual;
|
|
begin
|
|
{$IFDEF lclgtk2}
|
|
DebugLn(['gdk_gl_choose_visual not implemented yet for gtk2']);
|
|
RaiseGDBException('');
|
|
{$ENDIF}
|
|
|
|
if attrList=nil then begin
|
|
Result:=nil;
|
|
exit;
|
|
end;
|
|
|
|
dpy := GetDefaultXDisplay;
|
|
vi := glXChooseVisual(dpy,DefaultScreen(dpy), attrlist);
|
|
if (vi=nil) then begin
|
|
Result:=nil;
|
|
exit;
|
|
end;
|
|
|
|
visual := gdkx_visual_get(vi^.visualid);
|
|
XFree(vi);
|
|
Result:=visual;
|
|
end;
|
|
|
|
function gdk_gl_get_config(visual: PGdkVisual; attrib: longint): longint;
|
|
var
|
|
dpy: PDisplay;
|
|
vi: PXVisualInfo;
|
|
value: integer;
|
|
begin
|
|
Result:=-1;
|
|
if visual=nil then exit;
|
|
|
|
dpy := GetDefaultXDisplay;
|
|
|
|
vi := get_xvisualinfo(visual);
|
|
|
|
value:=0;
|
|
if (glXGetConfig(dpy, vi, attrib, value) = 0) then begin
|
|
XFree(vi);
|
|
Result:=value;
|
|
end else
|
|
XFree(vi);
|
|
end;
|
|
|
|
function gdk_gl_context_new(visual: PGdkVisual; attrlist: PlongInt): PGdkGLContext;
|
|
begin
|
|
Result := gdk_gl_context_share_new(visual, nil, GLXTrue, attrlist);
|
|
end;
|
|
|
|
function gdk_gl_context_share_new(visual: PGdkVisual; sharelist: PGdkGLContext;
|
|
direct: TGLBool; attrlist: plongint): PGdkGLContext;
|
|
var
|
|
dpy: PDisplay;
|
|
vi: PXVisualInfo;
|
|
PrivateShareList: PGdkGLContextPrivate;
|
|
PrivateContext: PGdkGLContextPrivate;
|
|
glxcontext: TGLXContext;
|
|
|
|
begin
|
|
Result:=nil;
|
|
dpy := GetDefaultXDisplay;
|
|
|
|
{$IFDEF lclgtk2}
|
|
if visual=nil then ;
|
|
vi:=glXChooseVisual(dpy, DefaultScreen(dpy), @attrList[0]);
|
|
{$ELSE}
|
|
if visual=nil then exit;
|
|
vi := get_xvisualinfo(visual);
|
|
{$ENDIF}
|
|
if vi=nil then
|
|
raise Exception.Create('gdk_gl_context_share_new no visual found');
|
|
|
|
PrivateShareList:=PGdkGLContextPrivate(sharelist);
|
|
|
|
if (sharelist<>nil) then
|
|
glxcontext := glXCreateContext(dpy, vi, PrivateShareList^.glxcontext,
|
|
direct)
|
|
else
|
|
glxcontext := glXCreateContext(dpy, vi, nil, direct);
|
|
|
|
XFree(vi);
|
|
if (glxcontext = nil) then exit;
|
|
|
|
PrivateContext := g_new(SizeOf(TGdkGLContextPrivate), 1);
|
|
PrivateContext^.xdisplay := dpy;
|
|
PrivateContext^.glxcontext := glxcontext;
|
|
PrivateContext^.ref_count := 1;
|
|
|
|
Result := PGdkGLContext(PrivateContext);
|
|
end;
|
|
|
|
function gdk_gl_context_attrlist_share_new(attrlist: Plongint;
|
|
sharelist: PGdkGLContext; direct: TGLBool): PGdkGLContext;
|
|
var
|
|
visual: PGdkVisual;
|
|
begin
|
|
{$IFDEF lclgtk2}
|
|
visual :=nil;
|
|
Result := gdk_gl_context_share_new(visual, sharelist, direct, attrlist);
|
|
{$ELSE}
|
|
visual := gdk_gl_choose_visual(attrlist);
|
|
if (visual <> nil) then
|
|
Result := gdk_gl_context_share_new(visual, sharelist, direct, attrlist)
|
|
else
|
|
Result := nil;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function gdk_gl_context_ref(context: PGdkGLContext): PGdkGLContext;
|
|
var
|
|
PrivateContext: PGdkGLContextPrivate;
|
|
begin
|
|
Result:=nil;
|
|
if context=nil then exit;
|
|
PrivateContext := PGdkGLContextPrivate(context);
|
|
inc(PrivateContext^.ref_count);
|
|
//DebugLn(['gdk_gl_context_ref ref_count=',PrivateContext^.ref_count]);
|
|
Result:=context;
|
|
end;
|
|
|
|
function gdk_gl_context_unref(context: PGdkGLContext):PGdkGLContext;
|
|
var
|
|
PrivateContext: PGdkGLContextPrivate;
|
|
begin
|
|
Result:=context;
|
|
g_return_if_fail(context<>nil,'');
|
|
|
|
PrivateContext:=PGdkGLContextPrivate(context);
|
|
|
|
dec(PrivateContext^.ref_count);
|
|
if (PrivateContext^.ref_count = 0) then begin
|
|
//DebugLn(['gdk_gl_context_unref START ref_count=',PrivateContext^.ref_count]);
|
|
if (PrivateContext^.glxcontext = glXGetCurrentContext()) then
|
|
glXMakeCurrent(PrivateContext^.xdisplay, None, nil);
|
|
glXDestroyContext(PrivateContext^.xdisplay, PrivateContext^.glxcontext);
|
|
PrivateContext^.glxcontext:=nil;
|
|
g_free(PrivateContext);
|
|
//DebugLn(['gdk_gl_context_unref END']);
|
|
Result:=nil;
|
|
end;
|
|
end;
|
|
|
|
function gdk_gl_make_current(drawable: PGdkDrawable;
|
|
context: PGdkGLContext): boolean;
|
|
var
|
|
PrivateContext: PGdkGLContextPrivate;
|
|
begin
|
|
Result:=false;
|
|
if drawable=nil then exit;
|
|
if context=nil then exit;
|
|
PrivateContext := PGdkGLContextPrivate(context);
|
|
|
|
Result:=boolean(glXMakeCurrent(PrivateContext^.xdisplay,
|
|
{$IFDEF LCLGTK}
|
|
GDK_WINDOW_XWINDOW(PGdkWindowPrivate(drawable)),
|
|
{$ELSE}
|
|
GDK_WINDOW_XWINDOW(drawable),
|
|
{$ENDIF}
|
|
PrivateContext^.glxcontext)
|
|
);
|
|
end;
|
|
|
|
procedure gdk_gl_swap_buffers(drawable: PGdkDrawable);
|
|
begin
|
|
g_return_if_fail(drawable <> nil);
|
|
|
|
glXSwapBuffers({$IFDEF LCLGTK}
|
|
GDK_WINDOW_XDISPLAY(PGdkWindowPrivate(drawable)),
|
|
GDK_WINDOW_XWINDOW(PGdkWindowPrivate(drawable))
|
|
{$ELSE}
|
|
GDK_WINDOW_XDISPLAY(drawable),
|
|
GDK_WINDOW_XWINDOW(drawable)
|
|
{$ENDIF}
|
|
);
|
|
end;
|
|
|
|
procedure gdk_gl_wait_gdk;
|
|
begin
|
|
glXWaitX;
|
|
end;
|
|
|
|
procedure gdk_gl_wait_gl;
|
|
begin
|
|
glXWaitGL;
|
|
end;
|
|
|
|
procedure gtk_gl_area_init(
|
|
{$IFDEF LCLGTK}
|
|
gl_area, theClass: Pointer
|
|
{$ELSE}
|
|
gl_area: PGTypeInstance; theClass: gpointer
|
|
{$ENDIF}
|
|
); cdecl;
|
|
begin
|
|
if theClass=nil then ;
|
|
//DebugLn(['gtk_gl_area_init START']);
|
|
PGtkGLArea(gl_area)^.glcontext:=nil;
|
|
{$IFDEF LclGtk2}
|
|
gtk_widget_set_double_buffered(PGtkWidget(gl_area),gdkFALSE);
|
|
GTK_WIDGET_UNSET_FLAGS(PGtkWidget(gl_area),GTK_NO_WINDOW);
|
|
{$ENDIF}
|
|
//DebugLn(['gtk_gl_area_init END']);
|
|
end;
|
|
|
|
function GTK_TYPE_GL_AREA: TGtkType;
|
|
const
|
|
gl_area_type_name = 'GtkGLArea';
|
|
gl_area_info: TGtkTypeInfo = (
|
|
type_name: gl_area_type_name;
|
|
object_size: SizeOf(TGtkGLArea);
|
|
class_size: SizeOf(TGtkGLAreaClass);
|
|
class_init_func: @gtk_gl_area_class_init;
|
|
object_init_func: @gtk_gl_area_init;
|
|
reserved_1: nil;
|
|
reserved_2: nil;
|
|
base_class_init_func: nil;
|
|
);
|
|
begin
|
|
if (gl_area_type=0) then begin
|
|
gl_area_type:=gtk_type_unique(gtk_drawing_area_get_type(),@gl_area_info);
|
|
end;
|
|
Result:=gl_area_type;
|
|
end;
|
|
|
|
function GTK_GL_AREA(obj: Pointer): PGtkGLArea;
|
|
begin
|
|
g_return_if_fail(GTK_IS_GL_AREA(obj),'');
|
|
Result:=PGtkGLArea(obj);
|
|
end;
|
|
|
|
function GTK_GL_AREA_CLASS(klass: Pointer): PGtkGLAreaClass;
|
|
begin
|
|
g_return_if_fail(GTK_IS_GL_AREA_CLASS(klass),'');
|
|
Result:=PGtkGLAreaClass(klass);
|
|
end;
|
|
|
|
function GTK_IS_GL_AREA(obj: Pointer): Boolean;
|
|
begin
|
|
{$IFDEF LCLGTK}
|
|
Result := Assigned(obj) and GTK_IS_GL_AREA_CLASS(PGtkTypeObject(obj)^.klass);
|
|
{$ELSE}
|
|
GTK_IS_GL_AREA:=GTK_CHECK_TYPE(obj,GTK_TYPE_GL_AREA);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function GTK_IS_GL_AREA_CLASS(klass: Pointer): Boolean;
|
|
begin
|
|
{$IFDEF LCLGTK}
|
|
Result := Assigned(klass) and (PGtkTypeClass(klass)^.thetype = GTK_TYPE_GL_AREA);
|
|
{$ELSE}
|
|
GTK_IS_GL_AREA_CLASS:=GTK_CHECK_CLASS_TYPE(klass,GTK_TYPE_GL_AREA);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function gtk_gl_area_get_type: TGtkType;
|
|
begin
|
|
Result:=GTK_TYPE_GL_AREA;
|
|
end;
|
|
|
|
function gtk_gl_area_new(Attribs: TContextAttribs): PGtkWidget;
|
|
begin
|
|
Result:=gtk_gl_area_share_new(Attribs, nil);
|
|
end;
|
|
|
|
{$IFDEF VerboseMultiSampling}
|
|
procedure WriteFBConfigID(const Prefix: string; PrivateContext: PGdkGLContextPrivate);
|
|
var
|
|
ctxValue: longint;
|
|
begin
|
|
ctxValue:=0;
|
|
debugln([Prefix,' ContextAttrib: ',
|
|
glXQueryContext(PrivateContext^.xdisplay, PrivateContext^.glxcontext, GLX_FBCONFIG_ID, ctxValue),
|
|
'-',ctxValue]);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function gtk_gl_area_share_new(Attribs: TContextAttribs; share: PGtkGLArea): PGtkWidget;
|
|
var
|
|
gl_area: PGtkGLArea;
|
|
begin
|
|
Result := nil;
|
|
//DebugLn(['gtk_gl_area_share_new START']);
|
|
if (share <> nil) and (not GTK_IS_GL_AREA(share)) then
|
|
exit;
|
|
gl_area:=gtk_gl_area_share_new_usefpglx(Attribs, share);
|
|
Result:=PGtkWidget(gl_area);
|
|
end;
|
|
|
|
function CustomXErrorHandler({%H-}para1:PDisplay; para2:PXErrorEvent):cint;cdecl;
|
|
begin
|
|
if para2^.error_code=8 then begin
|
|
raise Exception.Create('A BadMatch X error occurred. Most likely the requested OpenGL version is invalid.');
|
|
end;
|
|
Result:=0;
|
|
end;
|
|
|
|
function gtk_gl_area_share_new_usefpglx(Attribs: TContextAttribs; share: PGtkGLArea): PGtkGLArea;
|
|
var
|
|
GLArea: PGtkGLArea;
|
|
ShareList: PGdkGLContext;
|
|
PrivateShareList: PGdkGLContextPrivate;
|
|
ColorMap: PGdkColormap;
|
|
Visual: PGdkVisual;
|
|
PrivateContext: PGdkGLContextPrivate;
|
|
XDisplay: PDisplay;
|
|
XVInfo: PXVisualInfo;
|
|
ScreenNum: gint;
|
|
FBConfig: TGLXFBConfig;
|
|
FBConfigs: PGLXFBConfig;
|
|
FBConfigsCount: Integer;
|
|
Samples: cint;
|
|
BestSamples: Integer;
|
|
BestFBConfig: Integer;
|
|
GLXContext: TGLXContext;
|
|
i: Integer;
|
|
{ Used with glXCreateContextAttribsARB to select 3.X and above context }
|
|
Context3X: array [0..6] of Integer;
|
|
|
|
begin
|
|
Result:=nil;
|
|
ShareList:=nil;
|
|
if share<>nil then ShareList:=share^.glcontext;
|
|
PrivateShareList:=PGdkGLContextPrivate(ShareList);
|
|
XDisplay:=gdk_x11_get_default_xdisplay;
|
|
ScreenNum:=gdk_x11_get_default_screen;
|
|
if GLX_version_1_3(XDisplay) then begin
|
|
{ use approach recommended since glX 1.3 }
|
|
FBConfigsCount:=0;
|
|
FBConfigs:=glXChooseFBConfig(XDisplay, ScreenNum, @Attribs.AttributeList[0], FBConfigsCount);
|
|
if FBConfigsCount = 0 then
|
|
raise Exception.Create('Could not find FB config');
|
|
|
|
// if multisampling is requested try to get a number of sample buffers as
|
|
// close to the specified number as possible
|
|
if Attribs.MultiSampling>0 then begin
|
|
BestSamples:=0;
|
|
for i:=0 to FBConfigsCount-1 do begin
|
|
Samples:=0;
|
|
glXGetFBConfigAttrib(XDisplay, FBConfigs[i], GLX_SAMPLES_ARB, Samples);
|
|
if Samples=Attribs.MultiSampling then begin
|
|
BestFBConfig:=i;
|
|
break;
|
|
end else begin
|
|
if (Samples>BestSamples) and (Samples<Attribs.MultiSampling) then begin
|
|
BestSamples:=Samples;
|
|
BestFBConfig:=i;
|
|
end;
|
|
end;
|
|
end;
|
|
FBConfig := FBConfigs[BestFBConfig];
|
|
end else begin
|
|
{ just choose the first FB config from the FBConfigs list.
|
|
More involved selection possible. }
|
|
FBConfig := FBConfigs^;
|
|
end;
|
|
XVInfo:=glXGetVisualFromFBConfig(XDisplay, FBConfig);
|
|
end else begin
|
|
XVInfo:=glXChooseVisual(XDisplay, ScreenNum, @Attribs.AttributeList[0]);
|
|
end;
|
|
|
|
if XVInfo=nil then
|
|
raise Exception.Create('gdk_gl_context_share_new_usefpglx no visual found');
|
|
|
|
if GLX_version_1_3(XDisplay) then begin
|
|
if (GLX_ARB_create_context(XDisplay, DefaultScreen(XDisplay))) and
|
|
(Attribs.MajorVersion>0) then begin
|
|
// install custom X error handler
|
|
XSetErrorHandler(@CustomXErrorHandler);
|
|
Context3X[0]:=GLX_CONTEXT_MAJOR_VERSION_ARB;
|
|
Context3X[1]:=Attribs.MajorVersion;
|
|
Context3X[2]:=GLX_CONTEXT_MINOR_VERSION_ARB;
|
|
Context3X[3]:=Attribs.MinorVersion;
|
|
Context3X[4]:=GLX_CONTEXT_FLAGS_ARB;
|
|
Context3X[5]:=Attribs.ContextFlags;
|
|
Context3X[6]:=None;
|
|
if (ShareList<>nil) then begin
|
|
GLXContext:=glXCreateContextAttribsARB(XDisplay, FBConfig,
|
|
PrivateShareList^.glxcontext, true,
|
|
Context3X);
|
|
end else begin
|
|
GLXContext:=glXCreateContextAttribsARB(XDisplay, FBConfig, Nil, true,
|
|
Context3X);
|
|
end;
|
|
// restore default error handler
|
|
XSetErrorHandler(nil);
|
|
end else begin
|
|
if (ShareList<>nil) then begin
|
|
GLXContext:=glXCreateNewContext(XDisplay, FBConfig, GLX_RGBA_TYPE,
|
|
PrivateShareList^.glxcontext, True)
|
|
end else begin
|
|
GLXContext:=glXCreateNewContext(XDisplay, FBConfig, GLX_RGBA_TYPE, Nil,
|
|
True);
|
|
end;
|
|
end;
|
|
if FBConfigs<>nil then
|
|
XFree(FBConfigs);
|
|
end else begin
|
|
if (ShareList<>nil) then
|
|
GLXContext:=glXCreateContext(XDisplay, XVInfo, PrivateShareList^.glxcontext,
|
|
GLXTrue)
|
|
else
|
|
GLXContext:=glXCreateContext(XDisplay, XVInfo, Nil, GLXTrue);
|
|
end;
|
|
|
|
if GLXContext=nil then
|
|
raise Exception.Create('gdk_gl_context_share_new_usefpglx context creation failed');
|
|
|
|
ColorMap:=gdk_colormap_get_system;
|
|
Visual:=gdk_colormap_get_visual(ColorMap);
|
|
if XVisualIDFromVisual(
|
|
GDK_VISUAL_XVISUAL({$IFDEF LCLGTK}PGdkVisualPrivate(visual)
|
|
{$ELSE}visual
|
|
{$ENDIF}))
|
|
<>XVInfo^.visualid
|
|
then begin
|
|
Visual:=gdkx_visual_get(XVInfo^.visualid);
|
|
ColorMap:=gdk_colormap_new(Visual, {$IFDEF LCLGTK2}gFALSE{$ELSE}0{$ENDIF});
|
|
end;
|
|
|
|
GLArea:=gtk_type_new(gtk_gl_area_get_type);
|
|
gtk_widget_set_colormap(PGtkWidget(@GLArea^.darea), ColorMap);
|
|
|
|
PrivateContext:=g_new(SizeOf(TGdkGLContextPrivate), 1);
|
|
PrivateContext^.xdisplay:=XDisplay;
|
|
PrivateContext^.glxcontext:=GLXContext;
|
|
PrivateContext^.ref_count:=1;
|
|
|
|
GLArea^.glcontext:=PGdkGLContext(PrivateContext);
|
|
Result:=GLArea;
|
|
end;
|
|
|
|
function gtk_gl_area_make_current(glarea: PGtkGLArea): boolean;
|
|
begin
|
|
Result:=false;
|
|
if glarea=nil then exit;
|
|
if not GTK_IS_GL_AREA(glarea) then exit;
|
|
if not GTK_WIDGET_REALIZED(PGtkWidget(glarea)) then exit;
|
|
|
|
//DebugLn(['gtk_gl_area_make_current START']);
|
|
Result:=gdk_gl_make_current(PGtkWidget(glarea)^.window, glarea^.glcontext);
|
|
//DebugLn(['gtk_gl_area_make_current END']);
|
|
{$IFDEF VerboseMultiSampling}
|
|
//WriteFBConfigID('gtk_gl_area_make_current',PGdkGLContextPrivate(glarea^.glcontext));
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function gtk_gl_area_begingl(glarea: PGtkGLArea): boolean;
|
|
begin
|
|
Result:=gtk_gl_area_make_current(glarea);
|
|
end;
|
|
|
|
procedure gtk_gl_area_swap_buffers(gl_area: PGtkGLArea);
|
|
begin
|
|
g_return_if_fail(gl_area <> nil);
|
|
g_return_if_fail(GTK_IS_GL_AREA(gl_area));
|
|
g_return_if_fail(GTK_WIDGET_REALIZED(PGtkWidget(gl_area)));
|
|
|
|
gdk_gl_swap_buffers(GTK_WIDGET(gl_area)^.window);
|
|
end;
|
|
|
|
procedure LOpenGLViewport(Handle: HWND; Left, Top, Width, Height: integer);
|
|
begin
|
|
glViewport(Left,Top,Width,Height);
|
|
end;
|
|
|
|
procedure LOpenGLSwapBuffers(Handle: HWND);
|
|
begin
|
|
gtk_gl_area_swap_buffers({%H-}PGtkGLArea(Handle));
|
|
end;
|
|
|
|
function LOpenGLMakeCurrent(Handle: HWND): boolean;
|
|
var
|
|
Widget: PGtkWidget;
|
|
glarea: PGtkGLArea;
|
|
begin
|
|
if Handle=0 then
|
|
RaiseGDBException('LOpenGLSwapBuffers Handle=0');
|
|
Result:=false;
|
|
|
|
Widget:={%H-}PGtkWidget(PtrUInt(Handle));
|
|
glarea:=PGtkGLArea(Widget);
|
|
if not GTK_IS_GL_AREA(glarea) then
|
|
RaiseGDBException('LOpenGLSwapBuffers not a PGtkGLArea');
|
|
|
|
// make sure the widget is realized
|
|
gtk_widget_realize(Widget);
|
|
if not GTK_WIDGET_REALIZED(Widget) then exit;
|
|
|
|
// make current
|
|
Result:=gtk_gl_area_make_current(glarea);
|
|
end;
|
|
|
|
function LOpenGLReleaseContext(Handle: HWND): boolean;
|
|
var pd:PDIsplay;
|
|
begin
|
|
Result := false;
|
|
pd := glXGetCurrentDisplay();
|
|
if Assigned(pd) then
|
|
Result := glXMakeCurrent(pd, 0, nil);
|
|
end;
|
|
|
|
{$IFDEF LCLGtk2}
|
|
function gtkglarea_size_allocateCB(Widget: PGtkWidget; Size: pGtkAllocation;
|
|
Data: gPointer): GBoolean; cdecl;
|
|
const
|
|
CallBackDefaultReturn = {$IFDEF GTK2}false{$ELSE}true{$ENDIF};
|
|
var
|
|
SizeMsg: TLMSize;
|
|
GtkWidth, GtkHeight: integer;
|
|
LCLControl: TWinControl;
|
|
begin
|
|
Result := CallBackDefaultReturn;
|
|
if not GTK_WIDGET_REALIZED(Widget) then begin
|
|
// the widget is not yet realized, so this GTK resize was not a user change.
|
|
// => ignore
|
|
exit;
|
|
end;
|
|
if Size=nil then ;
|
|
LCLControl:=TWinControl(Data);
|
|
if LCLControl=nil then exit;
|
|
//DebugLn(['gtkglarea_size_allocateCB ',DbgSName(LCLControl)]);
|
|
|
|
gtk_widget_get_size_request(Widget, @GtkWidth, @GtkHeight);
|
|
|
|
SizeMsg.Msg:=0;
|
|
FillChar(SizeMsg,SizeOf(SizeMsg),0);
|
|
with SizeMsg do
|
|
begin
|
|
Result := 0;
|
|
Msg := LM_SIZE;
|
|
SizeType := Size_SourceIsInterface;
|
|
Width := SmallInt(GtkWidth);
|
|
Height := SmallInt(GtkHeight);
|
|
end;
|
|
//DebugLn(['gtkglarea_size_allocateCB ',GtkWidth,',',GtkHeight]);
|
|
LCLControl.WindowProc(TLMessage(SizeMsg));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function LOpenGLCreateContextCore(AWinControl: TWinControl;
|
|
WSPrivate: TWSPrivateClass; SharedControl: TWinControl;
|
|
DoubleBuffered, RGBA, DebugContext: boolean;
|
|
const RedBits, GreenBits, BlueBits, MajorVersion, MinorVersion,
|
|
MultiSampling, AlphaBits, DepthBits, StencilBits, AUXBuffers: Cardinal;
|
|
const AParams: TCreateParams): HWND;
|
|
var
|
|
NewWidget: PGtkWidget;
|
|
SharedArea: PGtkGLArea;
|
|
Attribs: TContextAttribs;
|
|
begin
|
|
if WSPrivate=nil then ;
|
|
{$IFDEF VerboseMultiSampling}
|
|
debugln(['LOpenGLCreateContextCore MultiSampling=',MultiSampling]);
|
|
{$ENDIF}
|
|
Attribs.AttributeList:=CreateOpenGLContextAttrList(DoubleBuffered,RGBA,RedBits,GreenBits,
|
|
BlueBits,AlphaBits,DepthBits,StencilBits,AUXBuffers);
|
|
Attribs.MajorVersion:=MajorVersion;
|
|
Attribs.MinorVersion:=MinorVersion;
|
|
|
|
// fill in context flags
|
|
Attribs.ContextFlags:=0;
|
|
if DebugContext then
|
|
Attribs.ContextFlags:=Attribs.ContextFlags or GLX_CONTEXT_DEBUG_BIT_ARB;
|
|
|
|
if MultiSampling>1 then begin
|
|
Attribs.MultiSampling:=MultiSampling;
|
|
end else begin
|
|
Attribs.MultiSampling:=0;
|
|
end;
|
|
try
|
|
if SharedControl<>nil then begin
|
|
SharedArea:={%H-}PGtkGLArea(PtrUInt(SharedControl.Handle));
|
|
if not GTK_IS_GL_AREA(SharedArea) then
|
|
RaiseGDBException('LOpenGLCreateContext');
|
|
NewWidget:=gtk_gl_area_share_new(Attribs,SharedArea);
|
|
end else begin
|
|
NewWidget:=gtk_gl_area_new(Attribs);
|
|
end;
|
|
Result:=HWND({%H-}PtrUInt(Pointer(NewWidget)));
|
|
PGtkobject(NewWidget)^.flags:=PGtkobject(NewWidget)^.flags or GTK_CAN_FOCUS;
|
|
{$IFDEF LCLGtk}
|
|
TGTKWidgetSet(WidgetSet).FinishCreateHandle(AWinControl,NewWidget,AParams);
|
|
{$ELSE}
|
|
TGTK2WidgetSet(WidgetSet).FinishCreateHandle(AWinControl,NewWidget,AParams);
|
|
g_signal_connect_after(PGtkObject(NewWidget), 'size-allocate',
|
|
TGTKSignalFunc(@gtkglarea_size_allocateCB), AWinControl);
|
|
{$ENDIF}
|
|
finally
|
|
FreeMem(Attribs.AttributeList);
|
|
end;
|
|
end;
|
|
|
|
function LOpenGLCreateContext(AWinControl: TWinControl;
|
|
WSPrivate: TWSPrivateClass; SharedControl: TWinControl;
|
|
DoubleBuffered, RGBA, DebugContext: boolean;
|
|
const RedBits, GreenBits, BlueBits, MajorVersion, MinorVersion,
|
|
MultiSampling, AlphaBits, DepthBits, StencilBits, AUXBuffers: Cardinal;
|
|
const AParams: TCreateParams): HWND;
|
|
begin
|
|
{$IFDEF VerboseMultiSampling}
|
|
debugln(['LOpenGLCreateContext MultiSampling=',MultiSampling]);
|
|
{$ENDIF}
|
|
if (MultiSampling > 1) and
|
|
GLX_ARB_multisample(GetDefaultXDisplay, DefaultScreen(GetDefaultXDisplay))
|
|
then begin
|
|
{$IFDEF VerboseMultiSampling}
|
|
debugln(['LOpenGLCreateContext GLX_ARB_multisample succeeded']);
|
|
{$ENDIF}
|
|
try
|
|
Result := LOpenGLCreateContextCore(AWinControl, WSPrivate, SharedControl,
|
|
DoubleBuffered, RGBA, DebugContext, RedBits, GreenBits, BlueBits, MajorVersion,
|
|
MinorVersion, MultiSampling, AlphaBits, DepthBits, StencilBits,
|
|
AUXBuffers, AParams);
|
|
except
|
|
{$IFDEF VerboseMultiSampling}
|
|
debugln(['LOpenGLCreateContext LOpenGLCreateContextCore failed, trying without multisampling']);
|
|
{$ENDIF}
|
|
{ retry without MultiSampling }
|
|
Result := LOpenGLCreateContextCore(AWinControl, WSPrivate, SharedControl,
|
|
DoubleBuffered, RGBA, DebugContext, RedBits, GreenBits, BlueBits, MajorVersion,
|
|
MinorVersion, 1, AlphaBits, DepthBits, StencilBits, AUXBuffers, AParams);
|
|
end;
|
|
end else begin
|
|
{ no multi-sampling requested (or GLX_ARB_multisample not available),
|
|
just pass to LOpenGLCreateContextCore }
|
|
Result := LOpenGLCreateContextCore(AWinControl, WSPrivate, SharedControl,
|
|
DoubleBuffered, RGBA, DebugContext, RedBits, GreenBits, BlueBits, MajorVersion,
|
|
MinorVersion, MultiSampling, AlphaBits, DepthBits, StencilBits,
|
|
AUXBuffers, AParams);
|
|
end;
|
|
end;
|
|
|
|
procedure LOpenGLDestroyContextInfo(AWinControl: TWinControl);
|
|
begin
|
|
if not AWinControl.HandleAllocated then exit;
|
|
// nothing to do
|
|
end;
|
|
|
|
function CreateOpenGLContextAttrList(DoubleBuffered: boolean; RGBA: boolean;
|
|
const RedBits, GreenBits, BlueBits, AlphaBits, DepthBits, StencilBits,
|
|
AUXBuffers: Cardinal): PInteger;
|
|
var
|
|
p: integer;
|
|
UseFBConfig: boolean;
|
|
|
|
procedure Add(i: integer);
|
|
begin
|
|
if Result<>nil then
|
|
Result[p]:=i;
|
|
inc(p);
|
|
end;
|
|
|
|
procedure CreateList;
|
|
begin
|
|
p:=0;
|
|
if UseFBConfig then begin
|
|
Add(GLX_X_RENDERABLE); Add(1);
|
|
Add(GLX_X_VISUAL_TYPE); Add(GLX_TRUE_COLOR);
|
|
end;
|
|
if DoubleBuffered then
|
|
begin
|
|
if UseFBConfig then
|
|
begin Add(GLX_DOUBLEBUFFER); Add(1); end else
|
|
Add(GLX_DOUBLEBUFFER);
|
|
end;
|
|
if RGBA then
|
|
begin
|
|
if not UseFBConfig then Add(GLX_RGBA);
|
|
{ For UseFBConfig, glXChooseFBConfig already defaults to RGBA }
|
|
end;
|
|
Add(GLX_RED_SIZE); Add(RedBits);
|
|
Add(GLX_GREEN_SIZE); Add(GreenBits);
|
|
Add(GLX_BLUE_SIZE); Add(BlueBits);
|
|
if AlphaBits>0 then
|
|
begin
|
|
Add(GLX_ALPHA_SIZE); Add(AlphaBits);
|
|
end;
|
|
if DepthBits>0 then
|
|
begin
|
|
Add(GLX_DEPTH_SIZE); Add(DepthBits);
|
|
end;
|
|
if StencilBits>0 then
|
|
begin
|
|
Add(GLX_STENCIL_SIZE); Add(StencilBits);
|
|
end;
|
|
if AUXBuffers>0 then
|
|
begin
|
|
Add(GLX_AUX_BUFFERS); Add(AUXBuffers);
|
|
end;
|
|
|
|
Add(0); { 0 = X.None (be careful: GLX_NONE is something different) }
|
|
end;
|
|
|
|
begin
|
|
{$IFDEF VerboseMultiSampling}
|
|
debugln(['CreateOpenGLContextAttrList MultiSampling=',MultiSampling]);
|
|
{$ENDIF}
|
|
UseFBConfig := GLX_version_1_3(GetDefaultXDisplay);
|
|
Result:=nil;
|
|
CreateList;
|
|
GetMem(Result,SizeOf(integer)*p);
|
|
CreateList;
|
|
end;
|
|
|
|
end.
|
|
|