# This program looks at the OpenGL installation as defined in perldl.conf
# when the Makefile is built.
# and builds the perl pdl interface based on the information found therein
# It preprocesses the include files with the cpp to try to get the prototypes
# as they are defined for the machine architecture. Bits of this code can be
# traced back to OpenGL-0.4, Tk, and many others who came before.
#
# Jim Edwards 7/18/2000
#
use Getopt::Long;
use strict;
use Config;
my (@incs,@defines);
GetOptions("I=s@" => \@incs,
"D=s@" => \@defines );
#
my @funcs = qw|gl.h glx.h glu.h|;
my @types = qw|X.h gl.h glx.h glu.h glxtokens.h|;
my $verbose;
my $pmdocs;
#
# These are prototypes which for one reason or another (hopefully documented
# further below) we dont want to generate.
#
my @dontfuncs;
my @oosubs; # holds subroutine prototypes for oo interface
my %dontfuncs;
for(@dontfuncs) {$dontfuncs{$_} = 1}
my(@path);
foreach(@incs){
if(-d "$_"){
push(@path,$_);
}else{
die "ERROR: directory $_ not found, fix perldl.conf and rerun perl Makefile.PL";
}
}
foreach(@defines){
$_ = "-D$_";
}
push(@path,"/usr/include");
my %consts; # filled by sub getconsts
my @subnames; # filled by sub getfuncs
my %t=(
'int' => 'INT',
'float' => 'FLOAT',
'double' => 'DOUBLE',
'short' => 'SHORT',
'long' => 'LONG',
'char' => 'CHAR',
'XID' => 'U_LONG',
'GLenum' => 'U_LONG',
'void *' => 'PTR',
'GLXFBConfig' => 'PTR',
'Display *' => 'PTR',
'Bool' => 'U_CHAR'
);
open(TYPEMAP,">typemap") or die "Can not write typemap\n";;
select TYPEMAP;
foreach my $file (@types){
my @file;
if($file=~/^gl/){
my $found;
foreach my $path (@path){
if(-e "$path/GL/$file"){
&getconsts("$path/GL/$file");
@file = &cpp("$path/GL/$file");
$found=1;
last;
}
}
unless($found){
print STDERR "WARNING: could not find file $file in $0 (may be okay)\n";
}
}elsif($file=~/^X/){
my $found;
foreach my $path (@path){
if(-e "$path/X11/$file"){
&getconsts("$path/X11/$file");
@file = &cpp("$path/X11/$file");
$found=1;
last;
}elsif(-e "/usr/include/X11/$file"){
&getconsts("/usr/include/X11/$file");
@file = &cpp("/usr/include/X11/$file");
$found=1;
last;
}
}
unless($found){
die "ERROR: Could not find $file in @path";
}
}
print STDERR "Calling gettypes for $file ",$#file,"\n" if($verbose);
gettypes(@file);
print "GLXContext T_PTR\n";
print "GLXFBConfigSGIX T_PTR\n";
}
while ( my ($key, $val) = each %t ) {
print "$key T_$val\n"
};
close TYPEMAP;
select STDOUT;
pp_addhdr('
#include <X11/X.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <stdio.h>
#define NUM_ARG 6
static int debug=0;
static int default_attributes[] = { GLX_RGBA, /*GLX_DOUBLEBUFFER,*/ None };
static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}
');
#
# generate the xs code
#
my $ppcode = "pp_addxs('','\n";
$ppcode .= glpcopenwindow();
$ppcode .= "\n');\n\n";
foreach my $file (@funcs){
my @file;
if($file=~/^gl/){
my $found;
foreach my $path (@path){
if(-e "$path/GL/$file"){
@file = cpp("$path/GL/$file");
$found=1;
last;
}
}
unless($found){
print STDERR "WARNING: could not find file $file in $0 (may be okay)\n";
}
}
$ppcode .= getfuncs(@file);
}
$ppcode .= pmstuff(@oosubs);
$ppcode .= exports(\%consts);
#
# print the code out so that we can refer to this
# file in the case of any errors
#
$ppcode .= "pp_addpm(<<'EODOCS');\n";
$ppcode .= "\n=head2 OpenGL Interface Functions\n\n";
$ppcode .= "=over 4\n\nThe following is a list of OpenGL functions for which an interface was created. Please refer to the OpenGL documentation for descriptions.\n\n";
$ppcode .= $pmdocs;
$ppcode .= "=back\n\n=cut\n\nEODOCS\n";
$ppcode .= "pp_done();";
open(F,">ppcode.out");
print F $ppcode;
close(F);
eval $ppcode;
sub cpp {
my ($file) = @_;
print STDERR "Running cpp on $file\n"if($verbose);
open(FILE,"$file") || die "Could not open file $file";
my @file = <FILE>;
close(FILE);
my $nfile = $file;
$nfile =~ s/.*\//tmp_/;
open(TFILE,">$nfile") || die "Could not write $nfile";
foreach(@file){
next if(/^\s*\#include\s+/); # this is dangerous
# as a consequence APIENTRY is undefined and glu routines are ignored
print TFILE $_;
}
close(TFILE);
#
# Put together a preprocessor command
#
my $com="$Config{cpprun} -P @defines";
foreach(split ' ', $Config{cppflags}){
$com .= " $_" if(/-D/);
}
$com .= " -D_LANGUAGE_C -DAPIENTRY=''"; # forces prototypes of SGI GL
open(FILE,"$com $nfile |") || die "cant open $com $nfile|\n";
@file = <FILE>;
close(FILE);
if ($verbose) {
use File::Basename;
my $out = basename($file).".cpp";
open FI, ">$out" ||
die "can't open temp output file $out";
print "outputting to $out...\n";
print FI "/* command: $com */\n";
print FI join('',@file);
close FI;
}
return @file;
}
sub gettypes {
my(@file) = @_;
my @tkeys = keys(%t);
foreach my $line (@file) {
if($line =~ /typedef/) {
my $cnt=0;
foreach my $k (@tkeys) {
if($line =~ /typedef\s+$k\s+(\w+)\s*\;/){
print "$1\t\tT_$t{$k}\n" ;
}elsif($line =~ /typedef\s+signed\s+$k\s+(\w+)\s*\;/){
print "$1\t\tT_$t{$k}\n" ;
}elsif($line =~ /typedef\s+unsigned\s+$k\s+(\w+)\s*\;/){
print "$1\t\tT_U_$t{$k}\n" ;
}elsif($line =~ /typedef\s+\w+\s+\(\s*\*\s*(\w+)\s*\)\s*\(.*\)\;/){
print "$1\t\tT_PTR\n" ;
}else{
$cnt++;
}
}
if($cnt > $#tkeys){
$line =~ /typedef\s+(.*)\s+(\w+)\s*\;/;
print STDERR "typedef Not Found >$line<$1<$2\n" if($verbose);
}
}
}
}
sub getfuncs {
my(@file) = @_;
my $outstr;
my $str = join ' ',@file;
# while($str =~ /extern\s+(\S[^\;]*)\s+(\w+)\s*\(([^\(\)]*)\)\s*\;/gs){
# my @vfuncs;
while($str =~ /extern\s+(\w+)\s+(\w+)\s*\(([^\(\)]*)\)\s*\;/gs){
my $rt=$1;
my $name=$2;
my $args=$3;
# $rt =~ s/^\s*const\s+//;
if($dontfuncs{$name}) { next }
if($args =~ /GLvoid\s*\*\s*\*/) { next }
push @subnames,$name;
print "Gen: $rt $name $args\n" if $verbose;
# push @vfuncs,"$rt,$name,V_$name,($args)";
#
# Ignore Display * when looking for pointers
#
if($args =~ /Display/ ||
$args =~ /GLXDrawable/ ||
$args =~ /GLXContext/){
push @oosubs,[$rt,$name,$args];
}
my $targs = $args;
$targs =~ s/Display\s*\*/long/g;
my $exists_pointer = ($targs =~ /\*/);
my @args = split(/\,/,$args);
@args=() if (($args =~ /^\s*void\s*$/) || ($args =~ /^\s*$/));
$pmdocs .= "=item * \n\n$name\n\n";
$outstr .= "pp_addxs('','\n";
$outstr .= "$rt\n";
$outstr .= "$name(";
my $i=0;
foreach my $a (@args) {
$outstr .= "," if($i);
$a =~ s/^\s*(.*\S)\s*$/$1/g;
$a =~ s/const\s+(\w+)\s+(\w+)\s*\[[^\]]*\]/$1_star $2/;
unless($a =~ s/^\s*Display\s*\*(\w+)/Display * $1/){
$a =~ s/(\w+)\s+(\w+)\s*\[[^\]]*\]/$1_star $2/;
$a =~ s/const\s+(.*\S)\s*\*\s*(.*)/$1_star $2/;
$a =~ s/(.*\S)\s*\*\s*(.*)/$1_star $2/;
}
$a =~ s/const\s+//;
$a =~ s/^\s*(\w+)\s*$/$1 arg$i/;
$a =~ /(.*)\s+(\w+)$/;
$outstr .= "$2";
$i++;
}
$outstr .= ")\n";
foreach my $a (@args) {
$a =~ /(.*)\s+(\w+)\s*$/;
my $t=$1;
my $n=$2;
$t =~ s/.*_star/char \*/;
$outstr .= "\t$t\t$n\n";
}
if($exists_pointer){
$outstr .= "\tCODE:\n";
$outstr .= "\t{\n";
$outstr .= "\t\tunsigned int err;\n";
$outstr .= "\t\t$name(";
my $i=0;
foreach my $a (@args) {
$outstr .= "," if($i);
$a =~ /(.*)\s+(\w+)\s*$/;
my $t=$1;
my $n=$2;
# print "HERE $t<>$n<\n";
if($t =~ /^(.*)_star/) {
$outstr .= "($1 *)";
}
$outstr .= $n;
$i++;
}
$outstr .= ");\n";
$outstr .= "\t\tif(debug) while((err = glGetError()) != GL_NO_ERROR){\n";
$outstr .= "\t\t\tprintf(\"ERROR issued in GL $name %s\\n\", gluErrorString(err));}\n";
$outstr .= "\t}\n";
}
$outstr .= "\n');\n\n";
}
return $outstr;
}
sub glpcopenwindow{
# just print this out verbatim to the xs
return sprintf <<'EOXS';
HV *
glpcOpenWindow(x,y,w,h,pw,event_mask, ...)
int x
int y
int w
int h
int pw
long event_mask
CODE:
{
Display *dpy;
Window win;
GLXContext ctx;
XVisualInfo *vi;
XSetWindowAttributes swa;
XEvent event;
Colormap cmap;
Window pwin=(Window)pw;
int *attributes = default_attributes;
unsigned int err;
RETVAL = newHV();
if(items>NUM_ARG){
int i;
attributes = (int *)malloc((items-NUM_ARG+1)* sizeof(int));
if(attributes==NULL){
return;
}
for(i=NUM_ARG;i<items;i++) {
attributes[i-NUM_ARG]=SvIV(ST(i));
}
attributes[items-NUM_ARG]=None;
}
if(debug){
int i;
for(i=0;attributes[i] != None; i++){
printf("att=%%d %%d\n",i,attributes[i]);
}
}
/* get a connection */
dpy = XOpenDisplay(NULL);
if (dpy==NULL){
printf("ERROR: failed to get an X connection\n");
return;
}else if(debug){
printf("Display open %%x\n",dpy);
}
/* get an appropriate visual */
vi = glXChooseVisual(dpy, DefaultScreen(dpy),attributes);
if(!vi) {
printf("ERROR: failed to get an X visual\n");
return;
}else if(debug){
printf("Visual open %%x\n",vi);
}
/* create a GLX context */
ctx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
if(!ctx) {
printf("ERROR: failed to get an X Context\n");
return;
}else if(debug){
printf("Context Created %%x\n",ctx);
}
/* create a color map */
cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
vi->visual, AllocNone);
/* create a window */
swa.colormap = cmap;
swa.border_pixel = 0;
swa.event_mask = event_mask;
if(!pwin){
pwin=RootWindow(dpy, vi->screen);
if(debug) printf("Using root as parent window 0x%%x\n",pwin);
}
if(x>=0) {
win = XCreateWindow(dpy, pwin, x, y, w, h, 0, vi->depth, InputOutput, vi->visual, CWBackPixel | CWBorderPixel|CWColormap|CWEventMask, &swa);
if(debug) printf("win = 0x%%x\n",win);
if(!win) {
return;
}
XMapWindow(dpy, win);
if(event_mask & StructureNotifyMask) {
XIfEvent(dpy, &event, WaitForNotify, (char*)win);
}
}
/* connect the context to the window */
if(!glXMakeCurrent(dpy, win, ctx)) {
return;
}
if(debug)
printf("Display=0x%%x Window=0x%%x Context=0x%%x\n",dpy,win,ctx);
hv_store(RETVAL, "Display", strlen("Display"), newSViv((IV) dpy),0);
hv_store(RETVAL, "Window", strlen("Window"), newSViv((IV) win),0);
hv_store(RETVAL, "Context", strlen("Context"), newSViv((IV) ctx),0);
hv_store(RETVAL, "GL_Version",strlen("GL_Version"),
newSVpv((char *) glGetString(GL_VERSION),0),0);
hv_store(RETVAL, "GL_Vendor",strlen("GL_Vendor"),
newSVpv((char *) glGetString(GL_VENDOR),0),0);
hv_store(RETVAL, "GL_Renderer",strlen("GL_Renderer"),
newSVpv((char *) glGetString(GL_RENDERER),0),0);
/* clear the buffer */
glClearColor(0,0,0,1);
while((err = glGetError()) != GL_NO_ERROR){
printf("ERROR issued in GL %%s\n", gluErrorString(err));
}
}
OUTPUT:
RETVAL
int
glpRasterFont(name,base,number,d)
char *name
int base
int number
Display *d
CODE:
{
XFontStruct *fi;
int lb;
fi = XLoadQueryFont(d,name);
if(fi == NULL) {
die("No font %s found",name);
}
lb = glGenLists(number);
if(lb == 0) {
die("No display lists left for font %s (need %d)",name,number);
}
glXUseXFont(fi->fid, base, number, lb);
RETVAL=lb;
}
OUTPUT:
RETVAL
void
glpSetDebug(flag)
int flag
CODE:
{
debug=flag;
}
void
glpPrintString(base,str)
int base
char *str
CODE:
{
glPushAttrib(GL_LIST_BIT);
glListBase(base);
glCallLists(strlen(str),GL_UNSIGNED_BYTE,(GLubyte*)str);
glPopAttrib();
}
int
XPending(d)
Display * d
int
XResizeWindow(d, w, x, y)
Display * d
Window w
int x
int y
void
glpXNextEvent(d)
void * d
PPCODE:
{
XEvent event;
char buf[10];
KeySym ks;
XNextEvent(d,&event);
switch(event.type) {
case ConfigureNotify:
EXTEND(sp,3);
PUSHs(sv_2mortal(newSViv(event.type)));
PUSHs(sv_2mortal(newSViv(event.xconfigure.width)));
PUSHs(sv_2mortal(newSViv(event.xconfigure.height)));
break;
case KeyPress:
case KeyRelease:
EXTEND(sp,2);
PUSHs(sv_2mortal(newSViv(event.type)));
XLookupString(&event.xkey,buf,sizeof(buf),&ks,0);
buf[0]=(char)ks;buf[1]=\'\0\';
PUSHs(sv_2mortal(newSVpv(buf,1)));
break;
case ButtonPress:
case ButtonRelease:
EXTEND(sp,7);
PUSHs(sv_2mortal(newSViv(event.type)));
PUSHs(sv_2mortal(newSViv(event.xbutton.button)));
PUSHs(sv_2mortal(newSViv(event.xbutton.x)));
PUSHs(sv_2mortal(newSViv(event.xbutton.y)));
PUSHs(sv_2mortal(newSViv(event.xbutton.x_root)));
PUSHs(sv_2mortal(newSViv(event.xbutton.y_root)));
PUSHs(sv_2mortal(newSViv(event.xbutton.state)));
break;
case MotionNotify:
EXTEND(sp,4);
PUSHs(sv_2mortal(newSViv(event.type)));
PUSHs(sv_2mortal(newSViv(event.xmotion.state)));
PUSHs(sv_2mortal(newSViv(event.xmotion.x)));
PUSHs(sv_2mortal(newSViv(event.xmotion.y)));
break;
case Expose:
default:
EXTEND(sp,1);
PUSHs(sv_2mortal(newSViv(event.type)));
break;
}
}
EOXS
}
sub pmstuff {
my(@oosubs) = @_;
my $outstr;
$outstr .= "pp_addpm(<<'CONSTANTS');\n";
foreach(sort keys %consts){
my $val = $consts{$_};
$val =~ s/\(int\)//;
$val =~ s/^(\D+)$/\'$1\'/;
$val = $consts{$val} if(defined $consts{$val});
$val =~ s/(\d)L/$1/;
$outstr .= "sub $_ () {$val}\n";
}
$outstr .= "CONSTANTS\n";
$outstr .= sprintf <<'END';
pp_addpm(<<'EOD');
=head1 NAME
PDL::Graphics::OpenGL -- a PDL interface to the OpenGL graphics library.
PDL::Graphics::OpenGLOO - an Object Oriented interface to the interface.
=head1 DESCRIPTION
This package implements an interface to various OpenGL or OpenGL
emulator libraries. Most of the interface is generated at PDL compile
time by the script opengl.pd which runs the c preprocessor on various
OpenGL include files to determine the correct C prototypes for each
configuration. The object oriented interface defines an Object which
contains the Display, Window and Context properties of the defined
OpenGL device. Any OpenGL function called from the OO interface will
recieve these fields from the object, they should not be passed explicitly.
This package is primarily intended for internal use by the
PDL::Graphics::TriD package, but should also be usable in its own right.
=head1 FUNCTIONS
=cut
package PDL::Graphics::OpenGL::OO;
use PDL::Options;
use strict;
my $debug;
#
# This is a list of all the fields of the opengl object and one could create a
# psuedo hash style object but I want to use multiple inheritence with Tk...
#
#use fields qw/Display Window Context Options GL_Vendor GL_Version GL_Renderer/;
=head2 new($class,$options)
Returns a new OpenGL object with attributes specified in the options
field. These attributes are:
=for ref
x,y - the position of the upper left corner of the window (0,0)
width,height - the width and height of the window in pixels (500,500)
parent - the parent under which the new window should be opened (root)
mask - the user interface mask (StructureNotifyMask)
attributes - attributes to pass to glXChooseVisual
=cut
sub new {
my($class_or_hash,$options) = @_;
my $isref = ref($class_or_hash);
my $p;
# PDL::Graphics::OpenGL::glpSetDebug(1);
if($isref and defined $class_or_hash->{Options}){
$p = $class_or_hash->{Options};
}else{
my $opt = new PDL::Options(default_options());
$opt->incremental(1);
$opt->options($options) if(defined $options);
$p = $opt->options;
}
my $self = PDL::Graphics::OpenGL::glpcOpenWindow(
$p->{x},$p->{y},$p->{width},$p->{height},
$p->{parent},$p->{mask},
@{$p->{attributes}});
if(ref($self) ne 'HASH'){
die "Could not create OpenGL window";
}
# psuedo-hash style see note above
# no strict 'refs';
# my $self = bless [ \%%{"$class\::FIELDS"}], $class;
#
$self->{Options} = $p;
if($isref){
if(defined($class_or_hash->{Options})){
return bless $self,ref($class_or_hash);
}else{
foreach(keys %%$self){
$class_or_hash->{$_} = $self->{$_};
}
return $class_or_hash;
}
}
bless $self,$class_or_hash;
}
=head2 default_options
default options for object oriented methods
=cut
#'
sub default_options{
{'x' => 0,
'y' => 0,
'width' => 500,
'height'=> 500,
'parent'=> 0,
'mask' => &PDL::Graphics::OpenGL::StructureNotifyMask,
'attributes'=> [&PDL::Graphics::OpenGL::GLX_RGBA,
&PDL::Graphics::OpenGL::GLX_RED_SIZE,1,
&PDL::Graphics::OpenGL::GLX_GREEN_SIZE,1,
&PDL::Graphics::OpenGL::GLX_BLUE_SIZE,1,
&PDL::Graphics::OpenGL::GLX_DOUBLEBUFFER
]
}
}
=head2 XPending()
OO interface to XPending
=cut
sub XPending {
my($self) = @_;
PDL::Graphics::OpenGL::XPending($self->{Display});
}
=head2 XResizeWindow(x,y)
OO interface to XResizeWindow
=cut
sub XResizeWindow {
my($self,$x,$y) = @_;
PDL::Graphics::OpenGL::XResizeWindow($self->{Display},$self->{Window},$x,$y);
}
=head2 glpXNextEvent()
OO interface to glpXNextEvent
=cut
sub glpXNextEvent {
my($self) = @_;
PDL::Graphics::OpenGL::glpXNextEvent($self->{Display});
}
=head2 glpRasterFont()
OO interface to the glpRasterFont function
=cut
sub glpRasterFont{
my($this,@args) = @_;
PDL::Graphics::OpenGL::glpRasterFont($args[0],$args[1],$args[2],$this->{Display});
}
=head2 AUTOLOAD
If the function is not prototyped in OO we assume there is
no explicit mention of the three identifying parameters (Display, Window, Context)
and try to load the OpenGL function.
=cut
sub AUTOLOAD {
my($self,@args) = @_;
use vars qw($AUTOLOAD);
my $sub = $AUTOLOAD;
return if($sub =~ /DESTROY/);
$sub =~ s/.*:://;
$sub = "PDL::Graphics::OpenGL::$sub";
if(defined $debug){
print "In AUTOLOAD: $sub at ",__FILE__," line ",__LINE__,".\n";
}
no strict 'refs';
return(&{$sub}(@args));
}
END
foreach(@oosubs){
my($rt,$name,$args) = @$_;
$outstr .= "\n=head2 $name\n\n";
$outstr .= "OO interface to the $name function\n\n=cut\n\n";
$outstr .= "sub $name {\n";
$outstr .= "\tmy(\$this,\@args) = \@_;\n";
$outstr .= "\t&PDL::Graphics::OpenGL::$name(";
my @args = split(/\,/,$args);
my $argcnt=0;
my $i=0;
foreach(@args){
$outstr .= "," if($i++);
if(/^\s*Display\s+\*/){
$outstr .= "\$this->{Display}";
}elsif(/^\s*GLXDrawable\s/){
$outstr .= "\$this->{Window}";
}elsif(/^\s*GLXContext\s/){
$outstr .= "\$this->{Context}";
}else{
$outstr .= "\$args[$argcnt]";
$argcnt++;
}
}
$outstr .= ");\n}\n";
}
$outstr .= "\nEOD\n";
return $outstr;
}
sub getconsts {
my ($file) = @_;
print STDERR "Getting constants from file $file\n" if($verbose);
open(FILE,$file) || die "cant open $file\n";
my @file = <FILE>;
close(FILE);
foreach my $line (@file) {
if($line =~ /^\#define\s+(\w+)\s+(\S+)\s*/) {
$consts{$1} = $2;
} elsif($line =~ /^\s*(\w+)\s*=\s*(\S+)\s*,\s*/) {
# Assume it's inside an enum.
$consts{$1} = $2;
}
}
}
sub exports{
my($consts) = @_;
my $outstr="pp_add_exported('','\n";
foreach (sort keys %$consts){
$outstr .= "\t\t$_\n";
}
foreach (sort @subnames){
$outstr .= "\t\t$_\n";
}
$outstr .= "');\n";
return $outstr;
}