lazarus/components/gtk/gtkglarea/openglgtkwidget.pas
mattias 0998146d41 added fpc replacement for gtkglarea
git-svn-id: trunk@6729 -
2005-02-03 23:16:59 +00:00

532 lines
16 KiB
ObjectPascal

{
*****************************************************************************
* *
* See the file COPYING.modifiedLGPL, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
Author: Mattias Gaertner
Abstract:
Provides methods and types to create gtkwidget with an opengl context.
Works very similar to gtkglarea, but completely in FreePascal, so no libs
needed.
}
unit OpenGLGtkWidget;
{$mode objfpc}{$H+}
interface
uses SysUtils, X, XUtil, XLib, glib, gdk, gtk, gl, nvGLX;
// gdkgl
const
// enum _GDK_GL_CONFIGS
GDK_GL_NONE = 0;
GDK_GL_USE_GL = 1;
GDK_GL_BUFFER_SIZE = 2;
GDK_GL_LEVEL = 3;
GDK_GL_RGBA = 4;
GDK_GL_DOUBLEBUFFER = 5;
GDK_GL_STEREO = 6;
GDK_GL_AUX_BUFFERS = 7;
GDK_GL_RED_SIZE = 8;
GDK_GL_GREEN_SIZE = 9;
GDK_GL_BLUE_SIZE = 10;
GDK_GL_ALPHA_SIZE = 11;
GDK_GL_DEPTH_SIZE = 12;
GDK_GL_STENCIL_SIZE = 13;
GDK_GL_ACCUM_RED_SIZE = 14;
GDK_GL_ACCUM_GREEN_SIZE = 15;
GDK_GL_ACCUM_BLUE_SIZE = 16;
GDK_GL_ACCUM_ALPHA_SIZE = 17;
// GLX_EXT_visual_info extension
GDK_GL_X_VISUAL_TYPE_EXT = $22;
GDK_GL_TRANSPARENT_TYPE_EXT = $23;
GDK_GL_TRANSPARENT_INDEX_VALUE_EXT = $24;
GDK_GL_TRANSPARENT_RED_VALUE_EXT = $25;
GDK_GL_TRANSPARENT_GREEN_VALUE_EXT = $26;
GDK_GL_TRANSPARENT_BLUE_VALUE_EXT = $27;
GDK_GL_TRANSPARENT_ALPHA_VALUE_EXT = $28;
type
TGdkGLContext = record end;
PGdkGLContext = ^TGdkGLContext;
// GLX_EXT_visual_info extension
function gdk_gl_query: boolean; cdecl;
function gdk_gl_get_info: string; cdecl;
function gdk_gl_choose_visual(attrlist: Plongint): PGdkVisual; cdecl;
function gdk_gl_get_config(visual: PGdkVisual; attrib: longint):longint; cdecl;
function gdk_gl_context_new(visual: PGdkVisual): PGdkGLContext; cdecl;
function gdk_gl_context_share_new(visual: PGdkVisual; sharelist: PGdkGLContext;
direct: Integer): PGdkGLContext; cdecl;
function gdk_gl_context_attrlist_share_new(attrlist: Plongint;
sharelist: PGdkGLContext; direct: Integer): PGdkGLContext; cdecl;
function gdk_gl_context_ref(context: PGdkGLContext): PGdkGLContext; cdecl;
procedure gdk_gl_context_unref(context:PGdkGLContext); cdecl;
function gdk_gl_make_current(drawable: PGdkDrawable;
context: PGdkGLContext): boolean; cdecl;
procedure gdk_gl_swap_buffers(drawable: PGdkDrawable); cdecl;
procedure gdk_gl_wait_gdk; cdecl;
procedure gdk_gl_wait_gl; cdecl;
{ glpixmap stuff }
type
TGdkGLPixmap = record end;
PGdkGLPixmap = ^TGdkGLPixmap;
//function gdk_gl_pixmap_new(visual:PGdkVisual; pixmap:PGdkPixmap):PGdkGLPixmap; cdecl;external;
//function gdk_gl_pixmap_ref(glpixmap:PGdkGLPixmap):PGdkGLPixmap; cdecl;external;
//procedure gdk_gl_pixmap_unref(glpixmap:PGdkGLPixmap); cdecl;external;
//function gdk_gl_pixmap_make_current(glpixmap:PGdkGLPixmap; context:PGdkGLContext):Integer; cdecl;external;
{ fonts }
//procedure gdk_gl_use_gdk_font(font:PGdkFont; first:longint; count:longint; list_base:longint); cdecl;external;
// gtkglarea
type
TGtkGlAreaMakeCurrentType = boolean;
PGtkGLArea = ^TGtkGLArea;
TGtkGLArea = record
darea: TGtkDrawingArea;
glcontext: PGdkGLContext;
end;
PGtkGLAreaClass = ^TGtkGLAreaClass;
TGtkGLAreaClass = record
parent_class: TGtkDrawingAreaClass;
end;
function GTK_TYPE_GL_AREA: TGtkType; cdecl;
function GTK_GL_AREA(obj: Pointer): PGtkGLArea; cdecl;
function GTK_GL_AREA_CLASS(klass: Pointer): PGtkGLAreaClass; cdecl;
function GTK_IS_GL_AREA(obj: Pointer): Boolean;
function GTK_IS_GL_AREA_CLASS(klass: Pointer): Boolean;
function gtk_gl_area_get_type: TGtkType; cdecl;
function gtk_gl_area_new(attrList: Plongint): PGtkWidget; cdecl;
function gtk_gl_area_share_new(attrList: Plongint; share: PGtkGLArea): PGtkWidget; cdecl;
//function gtk_gl_area_new_vargs(share:PGtkGLArea; args:array of const):PGtkWidget; cdecl;external;
//function gtk_gl_area_new_vargs(share:PGtkGLArea):PGtkWidget; cdecl;external;
function gtk_gl_area_make_current(glarea: PGtkGLArea): boolean; cdecl;
function gtk_gl_area_begingl(glarea: PGtkGLArea): boolean; cdecl;
procedure gtk_gl_area_swap_buffers(gl_area: PGtkGLArea);
implementation
var
gl_area_type: TGtkType = 0;
parent_class: Pointer = nil;
SharedArea: PGtkGLArea = nil;
type
TGdkGLContextPrivate = record
xdisplay: PDisplay;
glxcontext: TGLXContext;
ref_count: guint;
end;
PGdkGLContextPrivate = ^TGdkGLContextPrivate;
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
if ADisplay=nil then ;
Result:=gdk_screen;
end;
function g_new(BaseSize, Count: integer): Pointer;
begin
Result:=g_malloc(BaseSize*Count);
end;
function get_xvisualinfo(visual: PGdkVisualPrivate): PXVisualInfo;
// IMPORTANT: remember to XFree returned XVisualInfo !!!
var
vinfo_template: TXVisualInfo;
dpy: PDisplay;
nitems_return: integer;
vi: PXVisualInfo;
begin
dpy := GDK_DISPLAY;
// 'GLX uses VisualInfo records because they uniquely identify
// a (VisualID,screen,depth) tuple.'
vinfo_template.visual := GDK_VISUAL_XVISUAL(visual);
vinfo_template.visualid := XVisualIDFromVisual(vinfo_template.visual);
vinfo_template.depth := visual^.visual.depth;
vinfo_template.screen := DefaultScreen(dpy);
vi := XGetVisualInfo(dpy, VisualIDMask or VisualDepthMask or VisualScreenMask,
@vinfo_template, @nitems_return);
// visualinfo needs to be unique
if (vi=nil) then raise Exception.Create('');
if (nitems_return<>1) then raise Exception.Create('');
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);
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; cdecl;
begin
Result:=boolean(glXQueryExtension(GDK_DISPLAY,nil,nil)=true);
end;
function gdk_gl_get_info: string; cdecl;
begin
Result:='Vendor: '+glXGetClientString(GDK_DISPLAY, GLX_VENDOR)+LineEnding
+'Version: '+glXGetClientString(GDK_DISPLAY, GLX_VERSION)+LineEnding
+'Extensions: '+glXGetClientString(GDK_DISPLAY, GLX_EXTENSIONS)+LineEnding;
end;
function gdk_gl_choose_visual(attrlist: Plongint): PGdkVisual; cdecl;
var
dpy: PDisplay;
vi: PXVisualInfo;
visual: PGdkVisual;
begin
//writeln('gdk_gl_choose_visual A ');
if attrList=nil then begin
Result:=nil;
exit;
end;
//writeln('gdk_gl_choose_visual B ');
dpy := GDK_DISPLAY;
vi := glXChooseVisual(dpy,DefaultScreen(dpy), attrlist);
if (vi=nil) then begin
Result:=nil;
exit;
end;
//writeln('gdk_gl_choose_visual C ');
visual := gdkx_visual_get(vi^.visualid);
XFree(vi);
Result:=visual;
end;
function gdk_gl_get_config(visual: PGdkVisual; attrib: longint): longint; cdecl;
var
dpy: PDisplay;
vi: PXVisualInfo;
value: integer;
begin
Result:=-1;
if visual=nil then exit;
dpy := GDK_DISPLAY;
vi := get_xvisualinfo(PGdkVisualPrivate(visual));
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): PGdkGLContext; cdecl;
begin
Result:=gdk_gl_context_share_new(visual,nil,gtk_FALSE);
end;
function gdk_gl_context_share_new(visual: PGdkVisual; sharelist: PGdkGLContext;
direct: integer): PGdkGLContext; cdecl;
var
dpy: PDisplay;
vi: PXVisualInfo;
PrivateShareList: PGdkGLContextPrivate;
PrivateContext: PGdkGLContextPrivate;
glxcontext: TGLXContext;
begin
Result:=nil;
if visual=nil then exit;
dpy := GDK_DISPLAY;
vi := get_xvisualinfo(PGdkVisualPrivate(visual));
PrivateShareList:=PGdkGLContextPrivate(sharelist);
if (sharelist<>nil) then
glxcontext := glXCreateContext(dpy, vi, PrivateShareList^.glxcontext,
direct=gtk_True)
else
glxcontext := glXCreateContext(dpy, vi, nil, direct=gtk_True);
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: Integer): PGdkGLContext; cdecl;
var
visual: PGdkVisual;
begin
visual := gdk_gl_choose_visual(attrlist);
if (visual<>nil) then
Result:=gdk_gl_context_share_new(visual, sharelist, direct)
else
Result:=nil;
end;
function gdk_gl_context_ref(context: PGdkGLContext): PGdkGLContext; cdecl;
var
PrivateContext: PGdkGLContextPrivate;
begin
Result:=nil;
if context=nil then exit;
PrivateContext := PGdkGLContextPrivate(context);
inc(PrivateContext^.ref_count);
Result:=context;
end;
procedure gdk_gl_context_unref(context: PGdkGLContext); cdecl;
var
PrivateContext: PGdkGLContextPrivate;
begin
g_return_if_fail(context<>nil,'');
PrivateContext:=PGdkGLContextPrivate(context);
if (PrivateContext^.ref_count > 1) then
dec(PrivateContext^.ref_count)
else begin
if (PrivateContext^.glxcontext = glXGetCurrentContext()) then
glXMakeCurrent(PrivateContext^.xdisplay, None, nil);
glXDestroyContext(PrivateContext^.xdisplay, PrivateContext^.glxcontext);
g_free(PrivateContext);
end;
end;
function gdk_gl_make_current(drawable: PGdkDrawable;
context: PGdkGLContext): boolean; cdecl;
var
PrivateContext: PGdkGLContextPrivate;
begin
Result:=false;
if drawable=nil then exit;
if context=nil then exit;
PrivateContext := PGdkGLContextPrivate(context);
Result:=boolean(glXMakeCurrent(PrivateContext^.xdisplay,
GDK_WINDOW_XWINDOW(PGdkWindowPrivate(drawable)),
PrivateContext^.glxcontext)=true);
end;
procedure gdk_gl_swap_buffers(drawable: PGdkDrawable); cdecl;
begin
g_return_if_fail(drawable <> nil);
glXSwapBuffers(GDK_WINDOW_XDISPLAY(PGdkWindowPrivate(drawable)),
GDK_WINDOW_XWINDOW(PGdkWindowPrivate(drawable)));
end;
procedure gdk_gl_wait_gdk; cdecl;
begin
glXWaitX;
end;
procedure gdk_gl_wait_gl; cdecl;
begin
glXWaitGL;
end;
procedure gtk_gl_area_init(gl_area, theClass: Pointer); cdecl;
begin
if theClass=nil then ;
PGtkGLArea(gl_area)^.glcontext:=nil;
//#if GTK_CHECK_VERSION (1, 3, 1)
//gtk_widget_set_double_buffered(PGtkWidget(gl_area),gdkFALSE);
end;
function GTK_TYPE_GL_AREA: TGtkType; cdecl;
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; cdecl;
begin
g_return_if_fail(GTK_IS_GL_AREA(obj),'');
Result:=PGtkGLArea(obj);
end;
function GTK_GL_AREA_CLASS(klass: Pointer): PGtkGLAreaClass; cdecl;
begin
g_return_if_fail(GTK_IS_GL_AREA_CLASS(klass),'');
Result:=PGtkGLAreaClass(klass);
end;
function GTK_IS_GL_AREA(obj: Pointer): Boolean;
begin
// GTK_IS_GL_AREA:=GTK_CHECK_TYPE(obj,GTK_TYPE_GL_AREA);
Result := Assigned(obj) and GTK_IS_GL_AREA_CLASS(PGtkTypeObject(obj)^.klass);
end;
function GTK_IS_GL_AREA_CLASS(klass: Pointer): Boolean;
begin
// GTK_IS_GL_AREA_CLASS:=GTK_CHECK_CLASS_TYPE(klass,GTK_TYPE_GL_AREA);
Result := Assigned(klass) and (PGtkTypeClass(klass)^.thetype = GTK_TYPE_GL_AREA);
end;
function gtk_gl_area_get_type: TGtkType; cdecl;
begin
Result:=GTK_TYPE_GL_AREA;
end;
function gtk_gl_area_new(attrList: Plongint): PGtkWidget; cdecl;
var
Count: Integer;
CopyAttrList: Plongint;
Size: Integer;
begin
Count:=0;
while (attrList[Count]<>GDK_GL_NONE) do inc(Count);
inc(Count);
Size:=SizeOf(Integer)*Count;
GetMem(CopyAttrList,Size);
System.Move(attrList^,CopyAttrList^,Size);
Result:=gtk_gl_area_share_new(CopyAttrList,SharedArea);
FreeMem(CopyAttrList);
end;
function gtk_gl_area_share_new(attrList: Plongint; share: PGtkGLArea
): PGtkWidget; cdecl;
var
visual: PGdkVisual;
sharelist: PGdkGLContext;
glcontext: PGdkGLContext;
gl_area: PGtkGLArea;
begin
Result:=nil;
//writeln('gtk_gl_area_share_new A ');
if (share<>nil) and (not GTK_IS_GL_AREA(share)) then
exit;
{$IFNDEF win32}
//writeln('gtk_gl_area_share_new B ');
visual := gdk_gl_choose_visual(attrlist);
if (visual = nil) then exit;
{$ENDIF non win32}
//writeln('gtk_gl_area_share_new C ');
sharelist := nil;
if share<>nil then sharelist:=share^.glcontext;
glcontext := gdk_gl_context_share_new(visual, sharelist, gtk_TRUE);
if (glcontext = nil) then exit;
//writeln('gtk_gl_area_share_new D ');
{$IFNDEF win32}
// use colormap and visual suitable for OpenGL rendering
gtk_widget_push_colormap(gdk_colormap_new(visual,gtk_TRUE));
gtk_widget_push_visual(visual);
{$ENDIF non win32}
gl_area := gtk_type_new (gtk_gl_area_get_type);
gl_area^.glcontext := glcontext;
//writeln('gtk_gl_area_share_new E ',gl_area<>nil);
{$IFNDEF win32}
// pop back defaults
gtk_widget_pop_visual;
gtk_widget_pop_colormap;
{$ENDIF non win32}
Result:=PGtkWidget(gl_area);
end;
function gtk_gl_area_make_current(glarea: PGtkGLArea): boolean; cdecl;
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;
Result:=gdk_gl_make_current(PGtkWidget(glarea)^.window, glarea^.glcontext);
end;
function gtk_gl_area_begingl(glarea: PGtkGLArea): boolean; cdecl;
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;
end.