On Thu, 2008-04-17 at 08:33 -0700, Mark Glines via RT wrote:
> On Thu, 17 Apr 2008 08:16:23 -0700
> Geoffrey Broadwell <[EMAIL PROTECTED]> wrote:
> >   3. Parse the define *values*, and toss any that don't look right.
> > 
> > I'm thinking #3, since it's probably best to have that safety check
> > anyway, and it's probably the easiest.  I'll work on that one.
> 
> Sounds good to me.  Clearly those values weren't meant to actually be
> used anywhere.

OK, try the attached version of the patch.  The only thing different
than the old patch is a few lines in the code that parses the header
defines.  Now only integer hex constants and integer or floating point
decimal constants are allowed.  (Constants defined to be the same as
other constants still work.)  Any other value will be ignored, with an
optional message indicating the value that could not be parsed, if
--verbose is on.


-'f

=== CREDITS
==================================================================
--- CREDITS	(revision 4780)
+++ CREDITS	(local)
@@ -247,6 +247,7 @@
 D: Fix typo in PARROT_BIGENDIAN case
 
 N: Geoff Broadwell
+D: OpenGL binding
 D: examples fixes
 
 N: Gerard Goossen
=== MANIFEST
==================================================================
--- MANIFEST	(revision 4780)
+++ MANIFEST	(local)
@@ -266,6 +266,8 @@
 config/auto/memalign/test_c2.in                             []
 config/auto/msvc.pm                                         []
 config/auto/msvc/test_c.in                                  []
+config/auto/opengl.pm                                       []
+config/auto/opengl/opengl.in                                []
 config/auto/ops.pm                                          []
 config/auto/pack.pm                                         []
 config/auto/perldoc.pm                                      []
@@ -317,6 +319,7 @@
 config/gen/makefiles/pirc.in                                []
 config/gen/makefiles/root.in                                []
 config/gen/makefiles/tge.in                                 []
+config/gen/opengl.pm                                        []
 config/gen/parrot_include.pm                                []
 config/gen/platform.pm                                      []
 config/gen/platform/aix/asm.s                               []
@@ -653,6 +656,7 @@
 examples/nci/ls.pir                                         [main]doc
 examples/nci/sdl_blue_rectangle.pir                         [main]doc
 examples/nci/win32api.pir                                   [main]doc
+examples/opengl/triangle.pir                                [main]doc
 examples/pasm/cat.pasm                                      [main]doc
 examples/pasm/fact.pasm                                     [main]doc
 examples/pasm/hello.pasm                                    [main]doc
@@ -2595,6 +2599,8 @@
 runtime/parrot/library/JSON.pir                             [library]
 runtime/parrot/library/MIME/Base64.pir                      [library]
 runtime/parrot/library/Math/Random/mt19937ar.pir            [library]
+runtime/parrot/library/NCI/call_toolkit_init.pir            [library]
+runtime/parrot/library/OpenGL.pir                           [library]
 runtime/parrot/library/PCT/README                           [library]
 runtime/parrot/library/PGE/Dumper.pir                       [library]
 runtime/parrot/library/PGE/Glob.pir                         [library]
@@ -3468,6 +3474,7 @@
 t/steps/auto_msvc-02.t                                      []
 t/steps/auto_msvc-03.t                                      []
 t/steps/auto_msvc-04.t                                      []
+t/steps/auto_opengl-01.t                                    []
 t/steps/auto_ops-01.t                                       []
 t/steps/auto_pack-01.t                                      []
 t/steps/auto_perldoc-01.t                                   []
@@ -3504,6 +3511,7 @@
 t/steps/gen_core_pmcs-01.t                                  []
 t/steps/gen_languages-01.t                                  []
 t/steps/gen_makefiles-01.t                                  []
+t/steps/gen_opengl-01.t                                     []
 t/steps/gen_parrot_include-01.t                             []
 t/steps/gen_platform-01.t                                   []
 t/steps/init_defaults-01.t                                  []
=== MANIFEST.generated
==================================================================
--- MANIFEST.generated	(revision 4780)
+++ MANIFEST.generated	(local)
@@ -129,6 +129,10 @@
 runtime/parrot/dynext/gdbmhash.dll                [library]
 runtime/parrot/dynext/gdbmhash.dylib              [library]
 runtime/parrot/dynext/gdbmhash.so                 [library]
+runtime/parrot/dynext/libglutcb.bundle            [library]
+runtime/parrot/dynext/libglutcb.dll               [library]
+runtime/parrot/dynext/libglutcb.dylib             [library]
+runtime/parrot/dynext/libglutcb.so                [library]
 runtime/parrot/dynext/libnci_test.bundle          [library]
 runtime/parrot/dynext/libnci_test.dll             [library]
 runtime/parrot/dynext/libnci_test.dylib           [library]
@@ -202,6 +206,7 @@
 runtime/parrot/include/iterator.pasm              [main]
 runtime/parrot/include/longopt.pasm               [main]
 runtime/parrot/include/mmd.pasm                   [main]
+runtime/parrot/include/opengl_defines.pasm        [main]
 runtime/parrot/include/parrotlib.pbc              [main]
 runtime/parrot/include/pmctypes.pasm              [main]
 runtime/parrot/include/signal.pasm                [main]
@@ -220,6 +225,8 @@
 runtime/parrot/library/Data/Escape.pbc            [main]
 runtime/parrot/library/Data/Sort.pbc              [main]
 runtime/parrot/library/Getopt/Obj.pbc             [main]
+runtime/parrot/library/NCI/call_toolkit_init.pbc  [main]
+runtime/parrot/library/OpenGL.pbc                 [main]
 runtime/parrot/library/PAST-pm.pbc                [main]
 runtime/parrot/library/Parrot/Capture_PIR.pbc     [main]
 runtime/parrot/library/Parrot/Coroutine.pbc       [main]
@@ -277,6 +284,7 @@
 languages/pynie/pynie.pbc                         [main]
 languages/tcl/tcl.pbc                             [main]
 languages/tcl/runtime/tcllib.pbc                  [main]
+src/glut_callbacks.c                              [main]
 src/jit_emit.h                                    [main]include
 src/nci.c                                         [main]
 src/null_config.c                                 [main]
=== config/auto/opengl	(new directory)
==================================================================
=== config/auto/opengl/opengl.in
==================================================================
--- config/auto/opengl/opengl.in	(revision 4780)
+++ config/auto/opengl/opengl.in	(local)
@@ -0,0 +1,17 @@
+/* Copyright (C) 2008, The Perl Foundation. */
+/* $Id: $ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* XXXX: This location is platform dependent.  What's the preferred
+         way to find headers that move? */
+#include <GL/glut.h>
+
+
+int main(int argc, char *argv[])
+{
+    glutInit(&argc, argv);
+    printf("%d\n", GLUT_API_VERSION);
+    return EXIT_SUCCESS;
+}
=== config/auto/opengl.pm
==================================================================
--- config/auto/opengl.pm	(revision 4780)
+++ config/auto/opengl.pm	(local)
@@ -0,0 +1,128 @@
+# Copyright (C) 2008, The Perl Foundation.
+# $Id $
+
+=head1 NAME
+
+config/auto/opengl.pm - Probe for OpenGL, GLU, and GLUT libraries
+
+=head1 DESCRIPTION
+
+Determines whether the platform supports OpenGL, GLU and GLUT.  The optimal
+result at this time is to find OpenGL 2.1, GLU 1.3, and GLUT API version 4.
+
+=cut
+
+package auto::opengl;
+
+use strict;
+use warnings;
+use File::Spec;
+
+use base qw(Parrot::Configure::Step);
+
+use Parrot::Configure::Utils ':auto';
+
+sub _init {
+    my $self = shift;
+    my %data;
+    $data{description} = q{Determining if your platform supports OpenGL};
+    $data{result}      = q{};
+    return \%data;
+}
+
+sub runstep {
+    my ( $self, $conf ) = @_;
+
+    my ( $verbose, $without ) = $conf->options->get(
+        qw|
+            verbose
+            without-opengl
+        |
+    );
+
+    if ($without) {
+        $conf->data->set( has_opengl => 0 );
+        $self->set_result('no');
+        return 1;
+    }
+
+    my $cc        = $conf->data->get('cc');
+    my $libs      = $conf->data->get('libs');
+    my $linkflags = $conf->data->get('linkflags');
+    my $ccflags   = $conf->data->get('ccflags');
+
+    my $osname = $conf->data->get_p5('OSNAME');
+
+    _handle_mswin32($conf, $osname, $cc);
+
+    # On OS X check the presence of the OpenGL headers in the standard
+    # Fink/macports locations.
+    # XXXX: Mindlessly morphed from readline ... may need to be fixed
+    $self->_handle_darwin_for_fink    ($conf, $osname, 'GL/glut.h');
+    $self->_handle_darwin_for_macports($conf, $osname, 'GL/glut.h');
+
+    $conf->cc_gen('config/auto/opengl/opengl.in');
+    my $has_glut = 0;
+    eval { $conf->cc_build() };
+    if ( !$@ ) {
+        my $test = $conf->cc_run();
+        $has_glut = $self->_evaluate_cc_run($test, $verbose);
+        _handle_glut($conf, $has_glut);
+    }
+    unless ($has_glut) {
+        # The Parrot::Configure settings might have changed while class ran
+        $self->_recheck_settings($conf, $libs, $ccflags, $linkflags, $verbose);
+    }
+
+    return 1;
+}
+
+sub _handle_mswin32 {
+    my ($conf, $osname, $cc) = @_;
+    # XXXX: Mindlessly morphed from readline ... may need to be fixed
+    if ( $osname =~ /mswin32/i ) {
+        if ( $cc =~ /^gcc/i ) {
+            $conf->data->add( ' ', libs => '-lglut -lGLU -lGL' );
+        }
+        else {
+            $conf->data->add( ' ', libs => 'glut.lib glu.lib gl.lib' );
+        }
+    }
+    else {
+        $conf->data->add( ' ', libs => '-lglut -lGLU -lGL' );
+    }
+    return 1;
+}
+
+sub _evaluate_cc_run {
+    my ($self, $test, $verbose) = @_;
+    chomp $test;
+    my $has_glut = $test;
+    print " (yes, GLUT API $has_glut) " if $verbose;
+    $self->set_result("yes, GLUT $has_glut");
+    return $has_glut;
+}
+
+sub _handle_glut {
+    my ($conf, $has_glut) = @_;
+    $conf->data->set(
+        # XXXX: Completely cargo culted
+        opengl     => 'define',
+        has_opengl => $has_glut,
+        HAS_OPENGL => $has_glut,
+
+        glut       => 'define',
+        has_glut   => $has_glut,
+        HAS_GLUT   => $has_glut,
+    );
+    return 1;
+}
+
+1;
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
=== config/gen/makefiles/root.in
==================================================================
--- config/gen/makefiles/root.in	(revision 4780)
+++ config/gen/makefiles/root.in	(local)
@@ -194,6 +194,7 @@
     runtime/parrot/include/config.fpmc \
     $(SRC_DIR)/platform.c \
 #CONDITIONED_LINE(platform_asm):    $(SRC_DIR)/platform_asm.s \
+#CONDITIONED_LINE(has_glut):    $(SRC_DIR)/glut_callbacks.c \
     $(SRC_DIR)/core_pmcs.c \
     CFLAGS \
     $(IMCC_DIR)/CFLAGS \
@@ -209,6 +210,7 @@
 
 GEN_PASM_INCLUDES = \
     runtime/parrot/include/signal.pasm \
+#CONDITIONED_LINE(has_opengl):    runtime/parrot/include/opengl_defines.pasm \
     @TEMP_gen_pasm_includes@
 
 STICKY_FILES = \
@@ -271,7 +273,9 @@
     $(LIBRARY_DIR)/JSON.pbc \
     $(LIBRARY_DIR)/Math/Random/mt19937ar.pbc \
     $(LIBRARY_DIR)/MIME/Base64.pbc \
+    $(LIBRARY_DIR)/NCI/call_toolkit_init.pbc \
     $(LIBRARY_DIR)/ncurses.pbc \
+#CONDITIONED_LINE(has_opengl):    $(LIBRARY_DIR)/OpenGL.pbc \
     $(LIBRARY_DIR)/parrotlib.pbc \
     $(LIBRARY_DIR)/pcre.pbc \
     $(LIBRARY_DIR)/Parrot/Capture_PIR.pbc \
@@ -518,6 +522,7 @@
 # dynamic extensions
 DYNEXT_DIR          = runtime/parrot/dynext
 LIBNCI_TEST_SO      = $(DYNEXT_DIR)/libnci_test$(LOAD_EXT)
+LIBGLUTCB_SO        = $(DYNEXT_DIR)/libglutcb$(LOAD_EXT)
 
 ###############################################################################
 #
@@ -574,6 +579,7 @@
     runtime/parrot/include/config.fpmc \
     docs \
     $(LIBNCI_TEST_SO) \
+#CONDITIONED_LINE(has_glut):    $(LIBGLUTCB_SO) \
     $(GEN_LIBRARY) \
     dynpmc \
     dynoplibs \
@@ -1608,6 +1614,8 @@
     $(SRC_DIR)/exec_start$(O) \
     $(SRC_DIR)/nci_test$(O) \
     $(LIBNCI_TEST_SO) \
+    $(SRC_DIR)/glut_callbacks$(O) \
+    $(LIBGLUTCB_SO) \
     install_config.fpmc
 	$(PERL) $(BUILD_TOOLS_DIR)/c2str.pl --init
 	$(RM_F) \
@@ -1646,6 +1654,8 @@
     install_config.fpmc \
     $(SRC_DIR)/nci_test$(O) \
     $(LIBNCI_TEST_SO) \
+    $(SRC_DIR)/glut_callbacks$(O) \
+    $(LIBGLUTCB_SO) \
     $(LIBPARROT_STATIC) \
     $(LIBPARROT_SHARED)
 
@@ -2167,6 +2177,11 @@
 	$(LD) $(LD_LOAD_FLAGS) @ncilib_link_extra@ $(LDFLAGS) \
     @[EMAIL PROTECTED]@ $(SRC_DIR)/nci_test$(O) $(C_LIBS)
 
+# for use by runtime/parrot/library/OpenGL.pir
+$(LIBGLUTCB_SO): $(SRC_DIR)/glut_callbacks$(O)
+	$(LD) $(LD_LOAD_FLAGS) @ncilib_link_extra@ $(LDFLAGS) \
+    @[EMAIL PROTECTED]@ $(SRC_DIR)/glut_callbacks$(O) $(C_LIBS)
+
 # emacs etags
 # this needs exuberant-ctags
 
=== config/gen/opengl.pm
==================================================================
--- config/gen/opengl.pm	(revision 4780)
+++ config/gen/opengl.pm	(local)
@@ -0,0 +1,477 @@
+# Copyright (C) 2008, The Perl Foundation.
+# $Id: $
+
+=head1 NAME
+
+config/gen/opengl.pm - OpenGL binding generated files
+
+=head1 DESCRIPTION
+
+Generates several files used by the OpenGL binding.  These include:
+
+=over 4
+
+=item F<runtime/parrot/include/opengl_defines.pasm>
+
+=back
+
+=cut
+
+package gen::opengl;
+
+use strict;
+use warnings;
+
+use base qw(Parrot::Configure::Step);
+
+use Parrot::Configure::Utils ':gen';
+
+
+my @GLUT_1_CALLBACKS = (
+    [ 'Display',          'void' ],
+    [ 'Idle',             'void' ],
+    [ 'Entry',            'int state' ],
+    [ 'Menu State',       'int status' ],
+    [ 'Visibility',       'int state' ],
+    [ 'Motion',           'int x, int y' ],
+    [ 'Passive Motion',   'int x, int y' ],
+    [ 'Reshape',          'int width, int height' ],
+    [ 'Keyboard',         'unsigned char key, int x, int y' ],
+    [ 'Mouse',            'int button, int state, int x, int y' ],
+
+    # NOTE: Hardcoded because of special arguments
+    # [ 'Timer',            'int data' ],
+);
+
+my @GLUT_2_CALLBACKS = (
+    [ 'Button Box',       'int button, int state' ],
+    [ 'Dials',            'int dial, int value' ],
+    [ 'Spaceball Button', 'int button, int state' ],
+    [ 'Tablet Motion',    'int x, int y' ],
+    [ 'Spaceball Motion', 'int x, int y, int z' ],
+    [ 'Spaceball Rotate', 'int x, int y, int z' ],
+    [ 'Special',          'int key, int x, int y' ],
+    [ 'Tablet Button',    'int button, int state, int x, int y' ],
+);
+
+my @GLUT_3_CALLBACKS = (
+    [ 'Overlay Display',  'void' ],
+    [ 'Menu Status',      'int status, int x, int y' ],
+);
+
+my @GLUT_4_CALLBACKS = (
+    [ 'Close',            'void' ],
+    [ 'Menu Destroy',     'void' ],
+    [ 'WM Close',         'void' ],
+    [ 'Window Status',    'int state' ],
+    [ 'Keyboard Up',      'unsigned char key, int x, int y' ],
+    [ 'Special Up',       'int key, int x, int y' ],
+    [ 'Mouse Wheel',      'int wheel, int direction, int x, int y' ],
+
+    # NOTE: Hardcoded because of special arguments
+    # [ 'Joystick',         'int buttons, int xaxis, int yaxis, int zaxis' ],
+);
+
+my $MACRO_FILE = 'runtime/parrot/include/opengl_defines.pasm';
+my $C_FILE     = 'src/glut_callbacks.c';
+
+
+sub _init {
+    my $self = shift;
+
+    return {
+        description  => q{Generating OpenGL bindings},
+        result       => q{},
+    }
+}
+
+sub runstep {
+    my ( $self, $conf ) = @_;
+
+    unless ( $conf->data->get('has_opengl') ) {
+        $self->set_result('skipped');
+        return 1;
+    }
+
+    $self->gen_opengl_defines($conf);
+    $self->gen_glut_callbacks($conf);
+
+    return 1;
+}
+
+sub gen_opengl_defines {
+    my ( $self, $conf ) = @_;
+
+    my $verbose = $conf->options->get('verbose');
+
+    my @header_files = glob '/usr/include/GL/*.h';
+
+    my (%defs, @macros);
+    my $max_len = 0;
+
+    foreach my $file (@header_files) {
+        open my $header, '<', $file
+        or die "Could not open header '$file': $!";
+
+        while (<$header>) {
+            my (@F) = split;
+            next unless @F > 2 and $F[0] eq '#define';
+            next unless $F[1] =~ /^(GL(?:X|(?:UT?))?)_/;
+
+            $max_len = length $F[1] if $max_len < length $F[1];
+
+            my $api = $1;
+            if ($F[2] =~ /^GL/) {
+                push @macros, [$api, $F[1], $F[2]];
+            }
+            elsif (   $F[2] =~ /^0x[0-9a-fA-F]+$/
+                   || $F[2] =~ /^\d+(?:\.\d*)?(?:e\d+)?$/) {
+                $defs{$api}{$F[1]} = $F[2];
+            }
+            else {
+                print "\nUnable to parse '$F[2]'\n" if $verbose;
+            }
+        }
+    }
+
+    foreach my $macro (@macros) {
+        my ($api, $define, $value) = @$macro;
+        my ($val_api) = $value =~ /^(GL[A-Z]*)_/;
+
+        $defs{$api}{$define} = $defs{$val_api}{$value};
+
+        die "'$define' is defined as '$value', but no '$value' has been defined"
+        unless defined $defs{$val_api}{$value};
+    }
+
+    open my $macros, '>', $MACRO_FILE
+        or die "Could not open macro file '$MACRO_FILE' for write: $!";
+
+    print $macros <<"HEADER";
+# DO NOT EDIT THIS FILE.
+#
+# Any changes made here will be lost.
+#
+# This file is generated automatically by config/gen/opengl.pm
+# using the following files:
+#
+HEADER
+
+    print $macros "# $_\n" foreach sort @header_files;
+    print $macros "\n\n";
+
+    foreach my $api (sort keys %defs) {
+        my $api_defs = $defs{$api};
+
+        foreach my $define (sort keys %$api_defs) {
+        printf $macros ".macro_const %-${max_len}s %s\n",
+                       $define, $api_defs->{$define};
+        }
+    }
+
+    $conf->append_configure_log($MACRO_FILE);
+    
+    return 1;
+}
+
+sub gen_glut_callbacks {
+    my ( $self, $conf ) = @_;
+
+    my   $glut_api       = $conf->data->get('has_glut');
+    my   @glut_callbacks = @GLUT_1_CALLBACKS;
+    push @glut_callbacks,  @GLUT_2_CALLBACKS if $glut_api >= 2;
+    push @glut_callbacks,  @GLUT_3_CALLBACKS if $glut_api >= 3;
+    push @glut_callbacks,  @GLUT_4_CALLBACKS if $glut_api >= 4;
+
+    my @callbacks;
+    foreach my $raw (@glut_callbacks) {
+        my ($friendly, $params) = @$raw;
+
+        my $args   =  $params;
+           $args   =~ s/void//;
+           $args   =~ s/(^|, )(\w+ )+/$1/g;
+           $args   =  ", $args" if $args;
+        my $proto  =  $params;
+           $proto  =~ s/ \w+(,|$)/$1/g;
+        my $sig    =  $proto;
+           $sig    =~ s/void//;
+           $sig    =~ s/unsigned //;
+           $sig    =~ s/(\w)\w+\W*/$1/g;
+           $sig    =  "v$sig";
+
+        my $glutcb =  "glutcb${friendly}Func";
+           $glutcb =~ s/ //g;
+        my $glut   =  $glutcb;
+           $glut   =~ s/glutcb/glut/;
+        my $thunk  =  'glut_'    . lc($friendly) . '_func';
+           $thunk  =~ s/ /_/g;
+        my $enum   =  'GLUT_CB_' . uc($friendly);
+           $enum   =~ s/ /_/g;
+
+        push @callbacks, {
+            friendly  => $friendly,
+            params    => $params,
+            proto     => $proto,
+            args      => $args,
+            sig       => $sig,
+            glutcb    => $glutcb,
+            glut      => $glut,
+            thunk     => $thunk,
+            enum      => $enum,
+        };
+    }
+
+    my $enums     = '';
+    my $thunks    = '';
+    my $reg_funcs = '';
+    my $std_cbs   = '';
+
+    foreach (@callbacks) {
+        $enums     .= "    $_->{enum},\n";
+        $thunks    .= "           void          $_->{thunk}($_->{proto});\n";
+        $reg_funcs .= "PARROT_API void          $_->{glutcb}(PMC *, PMC *);\n";
+        $std_cbs   .= <<"IMPLEMENTATION"
+
+
+/*
+
+=item C<void $_->{glutcb}(interp_pmc, sub)>
+
+Register a Sub PMC to handle GLUT $_->{friendly} callbacks.
+
+=cut
+
+*/
+
+void
+$_->{thunk}($_->{params})
+{
+    PMC *sub        = callback_data[$_->{enum}].sub;
+    PMC *interp_pmc = callback_data[$_->{enum}].interp_pmc;
+
+    Parrot_Interp interp = verify_safe(interp_pmc, sub);
+
+    if (interp)
+        Parrot_runops_fromc_args_event(interp, sub, "$_->{sig}"$_->{args});
+}
+
+PARROT_API
+void
+$_->{glutcb}(PMC *interp_pmc, PMC *sub)
+{
+    check_notnull_cb(interp_pmc, sub);
+
+    callback_data[$_->{enum}].sub        = sub;
+    callback_data[$_->{enum}].interp_pmc = interp_pmc;
+
+    if (sub == PMCNULL)
+        $_->{glut}(NULL);
+    else
+        $_->{glut}($_->{thunk});
+}
+IMPLEMENTATION
+    }
+
+
+    ###
+    ### ACTUALLY WRITE FILE
+    ###
+
+    open my $c_file, '>', $C_FILE
+        or die "Could not open '$C_FILE' for write: $!";
+
+    print $c_file <<"EOF";
+/*
+# DO NOT EDIT THIS FILE.
+#
+# Any changes made here will be lost.
+#
+# This file is generated automatically by config/gen/opengl.pm
+
+Copyright (C) 2008, The Perl Foundation.
+
+=head1 NAME
+
+$c_file - GLUT Callback Function Handling
+
+=head1 DESCRIPTION
+
+GLUT callbacks are always synchronous and have void return type.  None
+of them accept user data parameters, so normal Parrot callback handling
+cannot be used.
+
+=head2 Functions
+
+=over 4
+
+=cut
+
+*/
+
+#include <GL/freeglut.h>
+#include "parrot/parrot.h"
+
+
+typedef enum {
+$enums
+    GLUT_CB_TIMER,
+
+#if GLUT_API_VERSION >= 4
+    GLUT_CB_JOYSTICK,
+#endif
+
+    GLUT_NUM_CALLBACKS
+} GLUT_CALLBACKS;
+
+typedef struct GLUT_CB_data {
+    PMC *sub;
+    PMC *interp_pmc;
+} GLUT_CB_data;
+
+GLUT_CB_data callback_data[GLUT_NUM_CALLBACKS];
+
+
+           Parrot_Interp verify_safe(PMC *, PMC *);
+           void          check_notnull_cb(PMC *, PMC *);
+
+           void          glut_timer_func(int);
+PARROT_API void          glutcbTimerFunc(PMC *, PMC *, unsigned int, int);
+
+#if GLUT_API_VERSION >= 4
+           void          glut_joystick_func(unsigned int, int, int, int);
+PARROT_API void          glutcbJoystickFunc(PMC *, PMC *, int);
+#endif
+
+$thunks
+$reg_funcs
+
+/* Never store a null interp in callback_data */
+void
+check_notnull_cb(PMC *interp_pmc, PMC *sub)
+{
+    if (PMC_IS_NULL(interp_pmc) || (Parrot_Interp) PMC_data(interp_pmc) == NULL)
+        PANIC((Parrot_Interp) PMC_data(interp_pmc),
+              "cannot register callback with null interpreter");
+}
+
+
+/* PANIC before running callback sub if interp or sub are insane;
+   return unwrapped Parrot_Interp if everything OK */
+Parrot_Interp
+verify_safe(PMC *interp_pmc, PMC *sub)
+{
+    Parrot_Interp interp = (Parrot_Interp) PMC_data(interp_pmc);
+
+    /* XXXX: Verify that interp still exists */
+
+    /* XXXX: Verify that sub exists in interp */
+
+    return PMC_IS_NULL(sub) ? NULL : interp;
+}
+
+
+/*
+
+# glutTimerFunc and glutJoystickFunc must be hardcoded because they have
+# special timer-related arguments that do not follow the template of all
+# of the other GLUT callbacks
+
+=item C<void glutcbTimerFunc(interp_pmc, sub, milliseconds, data)>
+
+Register a Sub PMC to handle GLUT Timer callbacks.
+
+=cut
+
+*/
+
+void
+glut_timer_func(int data)
+{
+    PMC *sub        = callback_data[GLUT_CB_TIMER].sub;
+    PMC *interp_pmc = callback_data[GLUT_CB_TIMER].interp_pmc;
+
+    Parrot_Interp interp = verify_safe(interp_pmc, sub);
+
+    if (interp)
+        Parrot_runops_fromc_args_event(interp, sub, "vi", data);
+}
+
+PARROT_API
+void
+glutcbTimerFunc(PMC *interp_pmc, PMC *sub, unsigned int milliseconds, int data)
+{
+    check_notnull_cb(interp_pmc, sub);
+
+    callback_data[GLUT_CB_TIMER].sub        = sub;
+    callback_data[GLUT_CB_TIMER].interp_pmc = interp_pmc;
+
+    if (sub == PMCNULL)
+        glutTimerFunc(0, NULL, 0);
+    else
+        glutTimerFunc(milliseconds, glut_timer_func, data);
+}
+
+
+#if GLUT_API_VERSION >= 4
+/*
+
+=item C<void glutcbJoystickFunc(interp_pmc, sub, pollinterval)>
+
+Register a Sub PMC to handle GLUT Joystick callbacks.
+
+=cut
+
+*/
+
+void
+glut_joystick_func(unsigned int buttons, int xaxis, int yaxis, int zaxis)
+{
+    PMC *sub        = callback_data[GLUT_CB_JOYSTICK].sub;
+    PMC *interp_pmc = callback_data[GLUT_CB_JOYSTICK].interp_pmc;
+
+    Parrot_Interp interp = verify_safe(interp_pmc, sub);
+
+    if (interp)
+        Parrot_runops_fromc_args_event(interp, sub, "viiii", buttons, xaxis, yaxis, zaxis);
+}
+
+PARROT_API
+void
+glutcbJoystickFunc(PMC *interp_pmc, PMC *sub, int pollinterval)
+{
+    check_notnull_cb(interp_pmc, sub);
+
+    callback_data[GLUT_CB_JOYSTICK].sub        = sub;
+    callback_data[GLUT_CB_JOYSTICK].interp_pmc = interp_pmc;
+
+    if (sub == PMCNULL)
+        glutJoystickFunc(NULL, 0);
+    else
+        glutJoystickFunc(glut_joystick_func, pollinterval);
+}
+#endif
+$std_cbs
+
+
+/*
+
+=back
+
+=cut
+
+*/
+EOF
+
+    $conf->append_configure_log($C_FILE);
+
+    return 1;
+}
+
+1;
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
=== examples/opengl	(new directory)
==================================================================
=== examples/opengl/triangle.pir
==================================================================
--- examples/opengl/triangle.pir	(revision 4780)
+++ examples/opengl/triangle.pir	(local)
@@ -0,0 +1,171 @@
+=head1 TITLE
+
+triangle.pir - Initialize GLUT and render a simple OpenGL animation
+
+=head1 SYNOPSIS
+
+    $ cd parrot-home
+    $ ./parrot examples/opengl/triangle.pir
+
+=head1 DESCRIPTION
+
+This simple example shows how to load the OpenGL/GLU/GLUT wrapper, create
+a small GLUT window and register the appropriate callbacks, and finally
+display a simple OpenGL animation until the user closes the window.
+
+To quit the example, press C<Q> or the C<ESCAPE> key, or close the window
+using your window manager (using the X in the corner of the window title
+bar, for example).  To pause or restart the animation, press any other
+ASCII key.
+
+=cut
+
+
+.include 'datatypes.pasm'
+.include 'opengl_defines.pasm'
+
+.sub main :main
+    .param pmc argv
+
+    load_bytecode 'library/OpenGL.pbc'
+    load_bytecode 'library/NCI/call_toolkit_init.pbc'
+
+    .local pmc glutInit, glutInitDisplayMode, glutCreateWindow, glutMainLoop
+    .local pmc glutcbDisplayFunc, glutcbIdleFunc
+    .local pmc glutcbKeyboardFunc, glutcbSpecialFunc
+    .local pmc call_toolkit_init
+    glutInit            = get_global ['OpenGL'], 'glutInit'
+    glutInitDisplayMode = get_global ['OpenGL'], 'glutInitDisplayMode'
+    glutCreateWindow    = get_global ['OpenGL'], 'glutCreateWindow'
+    glutMainLoop        = get_global ['OpenGL'], 'glutMainLoop'
+    glutcbDisplayFunc   = get_global ['OpenGL'], 'glutcbDisplayFunc'
+    glutcbIdleFunc      = get_global ['OpenGL'], 'glutcbIdleFunc'
+    glutcbKeyboardFunc  = get_global ['OpenGL'], 'glutcbKeyboardFunc'
+    glutcbSpecialFunc   = get_global ['OpenGL'], 'glutcbSpecialFunc'
+    call_toolkit_init   = get_global ['NCI'],    'call_toolkit_init'
+
+    argv = call_toolkit_init(glutInit, argv)
+
+    .local int mode
+    mode = .GLUT_DOUBLE | .GLUT_RGBA
+    glutInitDisplayMode(mode)
+
+    .local pmc window
+    window = new Integer
+    window = glutCreateWindow('Test')
+    set_global 'glut_window', window
+
+    .local pmc interp
+    interp = getinterp
+
+    .const .Sub draw     = 'draw'
+    .const .Sub idle     = 'idle'
+    .const .Sub keyboard = 'keyboard'
+    glutcbDisplayFunc (interp, draw)
+    glutcbIdleFunc    (interp, idle)
+    glutcbKeyboardFunc(interp, keyboard)
+
+    .local pmc rotating
+    rotating = new 'Integer'
+    rotating = 1
+    set_global 'rotating', rotating
+
+    .local pmc prev_time
+    .local num now
+    now       = time
+    prev_time = new 'Float'
+    prev_time = now
+    set_global 'prev_time', prev_time
+
+    glutMainLoop()
+.end
+
+.sub draw
+    .local pmc glClear, glFlush
+    .local pmc glBegin, glEnd
+    .local pmc glColor3f, glVertex3f
+    glClear    = get_global ['OpenGL'], 'glClear'
+    glFlush    = get_global ['OpenGL'], 'glFlush'
+    glBegin    = get_global ['OpenGL'], 'glBegin'
+    glEnd      = get_global ['OpenGL'], 'glEnd'
+    glColor3f  = get_global ['OpenGL'], 'glColor3f'
+    glVertex3f = get_global ['OpenGL'], 'glVertex3f'
+
+    .local pmc glutSwapBuffers
+    glutSwapBuffers = get_global ['OpenGL'], 'glutSwapBuffers'
+
+    .local int buffers
+    buffers = .GL_COLOR_BUFFER_BIT | .GL_DEPTH_BUFFER_BIT
+    glClear(buffers)
+
+    glBegin(.GL_TRIANGLES)
+
+    glColor3f(1, 0, 0)
+    glVertex3f(-.5, -.5, 0)
+    glColor3f(0, 1, 0)
+    glVertex3f( .5, -.5, 0)
+    glColor3f(0, 0, 1)
+    glVertex3f(0  ,  .5, 0)
+
+    glEnd()
+
+    glFlush()
+
+    glutSwapBuffers()
+.end
+
+.sub idle
+    .local pmc glutPostRedisplay
+    .local pmc glRotatef
+    glutPostRedisplay = get_global ['OpenGL'], 'glutPostRedisplay'
+    glRotatef         = get_global ['OpenGL'], 'glRotatef'
+
+    .local pmc prev_time
+    .local num prev, now, dt
+    prev_time  = get_global 'prev_time'
+    prev       = prev_time
+    now        = time
+    dt         = now - prev
+    dt        *= 360
+    prev_time  = now
+
+    .local pmc rotating
+    rotating = get_global 'rotating'
+    if rotating goto do_rotation
+    .return ()
+
+  do_rotation:
+    glRotatef(dt, 0, 1, 0)
+    glutPostRedisplay()
+.end
+
+.sub keyboard
+    .param int key
+    .param int x
+    .param int y
+
+    # For ESCAPE, 'Q', and 'q', exit program
+    if key ==  27 goto quit
+    if key ==  81 goto quit
+    if key == 113 goto quit
+    goto toggle_rotation
+  quit:
+    .local pmc glutDestroyWindow, glut_window
+    glutDestroyWindow = get_global ['OpenGL'], 'glutDestroyWindow'
+    glut_window       = get_global             'glut_window'
+    glutDestroyWindow(glut_window)
+    end
+
+    # For all other keys, just toggle rotation
+  toggle_rotation:
+    .local pmc rotating
+    rotating  = get_global 'rotating'
+    rotating  = not rotating
+.end
+
+
+# Local Variables:
+#   mode: pir
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4 ft=pir:
=== lib/Parrot/Configure/Options/Conf.pm
==================================================================
--- lib/Parrot/Configure/Options/Conf.pm	(revision 4780)
+++ lib/Parrot/Configure/Options/Conf.pm	(local)
@@ -78,6 +78,7 @@
     verbose-step
     version
     without-gdbm
+    without-opengl
     without-crypto
     without-gettext
     without-gmp
@@ -190,6 +191,7 @@
 
    --without-gmp        Build parrot without GMP support
    --without-gdbm       Build parrot without GDBM support
+   --without-opengl     Build parrot without OpenGL support (GL/GLU/GLUT)
    --without-crypto     Build parrot without crypto support (libssl)
 
 ICU Options:
=== lib/Parrot/Configure/Step/List.pm
==================================================================
--- lib/Parrot/Configure/Step/List.pm	(revision 4780)
+++ lib/Parrot/Configure/Step/List.pm	(local)
@@ -57,6 +57,7 @@
     auto::gmp
     auto::readline
     auto::gdbm
+    auto::opengl
     auto::crypto
     auto::gettext
     auto::snprintf
@@ -69,6 +70,7 @@
     gen::config_h
     gen::core_pmcs
     gen::parrot_include
+    gen::opengl
     gen::languages
     gen::makefiles
     gen::platform
=== lib/Parrot/Configure/Step/Methods.pm
==================================================================
--- lib/Parrot/Configure/Step/Methods.pm	(revision 4780)
+++ lib/Parrot/Configure/Step/Methods.pm	(local)
@@ -32,8 +32,8 @@
 
     $self->_recheck_settings($conf, $libs, $ccflags, $linkflags, $verbose);
 
-Currently used in configuration step classes auto::gmp, auto::readline and
-auto::gdbm.
+Currently used in configuration step classes auto::gmp, auto::readline,
+auto::gdbm, and auto::opengl.
 
 =cut
 
=== runtime/parrot/library/NCI	(new directory)
==================================================================
=== runtime/parrot/library/NCI/call_toolkit_init.pir
==================================================================
--- runtime/parrot/library/NCI/call_toolkit_init.pir	(revision 4780)
+++ runtime/parrot/library/NCI/call_toolkit_init.pir	(local)
@@ -0,0 +1,121 @@
+=head1 TITLE
+
+call_toolkit_init.pir - PIR code to call toolkits that alter argv
+
+=head1 SYNOPSIS
+
+    .sub main :main
+        .param pmc argv
+
+        # Load this library and the NCI wrapper for the toolkit
+	load_bytecode 'library/NCI/call_toolkit_init.pbc'
+	load_bytecode 'library/FooTK.pbc'
+
+	# Find Subs for toolkit's init function, and this helper routine
+        .local pmc fooInit, call_toolkit_init
+	fooInit           = get_global ['FooTK'], 'fooInit'
+	call_toolkit_init = get_global ['NCI'],   'call_toolkit_init'
+
+	# Call toolkit's init function, overwriting argv with mangled copy
+	argv = call_toolkit_init(fooInit, argv)
+
+	# Alternately, you can save both the original argv and mangled copy
+	.local pmc mangled_argv
+	mangled_argv = call_toolkit_init(fooInit, argv)
+
+        # ...
+    .end
+
+=head1 DESCRIPTION
+
+Calling an NCI toolkit init function that expects to parse and mangle
+argv is a relatively grotty bit of code that no one should have to write
+twice.  Hence this library, which provides just one routine:
+
+=over 4
+
+=item new_argv = call_toolkit_init(pmc init_func, pmc orig_argv)
+
+Call an NCI void function with params C<(&argc, argv)> (and thus having
+Parrot signature C<'v3p'>).  This is a very common signature for toolkit
+(AKA framework) init functions that want to filter out command line
+options that the toolkit itself should process.  Since it is expected
+that the init call will remove some arguments, C<call_toolkit_init>
+returns an updated argv.  C<orig_argv> is never changed; the NCI call is
+performed using a copy.
+
+=back
+
+=cut
+
+
+.namespace ['NCI']
+
+.include 'datatypes.pasm'
+
+
+.sub call_toolkit_init
+    .param pmc init_func
+    .param pmc orig_argv
+
+    # Calculate argc
+    .local int count
+    .local pmc argc
+    count = orig_argv
+    argc  = new 'Integer'
+    argc  = count
+
+    # Declare structure of a raw C string array with proper element count
+    .local pmc argv_decl
+    argv_decl = new 'ResizablePMCArray'
+    push argv_decl, .DATATYPE_CSTR
+    push argv_decl, count
+    push argv_decl, 0
+    # XXX: This is unportably wrong; it assumes sizeof(INT) = sizeof(PTR)
+    push argv_decl, .DATATYPE_INT
+    push argv_decl, 0
+    push argv_decl, 0
+ 
+    # Create C string array from struct declaration and Parrot string array
+    .local pmc argv
+    .local int i
+    argv = new 'ManagedStruct', argv_decl
+    i = 0
+  argv_loop:
+    unless i < count goto add_null
+    # It seems like this should be possible in one op
+    $S0 = orig_argv[i]
+    argv[0;i] = $S0
+    inc i
+    goto argv_loop
+  add_null:
+    # XXX: This is unportably wrong; it assumes sizeof(INT) = sizeof(PTR)
+    argv[1] = 0
+
+    # Call the NCI framework init function
+    init_func(argc, argv)
+
+    # Build a new_argv array to match the init function's return
+    .local int new_count
+    .local pmc new_argv
+    new_count = argc
+    new_argv  = new 'ResizableStringArray'
+    i = 0
+  new_argv_loop:
+    unless i < new_count goto done
+    $S0 = argv[0;i]
+    push new_argv, $S0
+    inc i
+    goto new_argv_loop
+
+    # Finally, return the new (and adjusted) argv
+  done:
+    .return (new_argv)
+.end
+
+
+# Local Variables:
+#   mode: pir
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4 ft=pir:
=== runtime/parrot/library/OpenGL.pir
==================================================================
--- runtime/parrot/library/OpenGL.pir	(revision 4780)
+++ runtime/parrot/library/OpenGL.pir	(local)
@@ -0,0 +1,304 @@
+=head1 NAME
+
+OpenGL - Parrot extension for OpenGL bindings
+
+=head1 SYNOPSIS
+
+TBD
+
+=head1 DESCRIPTION
+
+This library is a straightforward Parrot NCI wrapper for OpenGL, GLU, and
+GLUT.  It is still a work in progress; work will generally start with the
+oldest, most widely supported functions and progress to the most recently
+standardized calls.
+
+=head2 Initialization
+
+The initialization routines are mostly for internal use only.  They
+include:
+
+=over 4
+
+=cut
+
+
+.namespace ['OpenGL']
+
+.include 'datatypes.pasm'
+.include 'iterator.pasm'
+
+
+=item _opengl_init()
+
+At module load time, calls the other initialization routines in the proper
+order.
+
+=cut
+
+.sub _opengl_init :load
+    _load_opengl_libs()
+    _wrap_all_opengl_entry_points()
+.end
+
+
+=item _load_opengl_libs()
+
+Opens all relevent OpenGL system libraries.  For portability, tries each of
+the known different filenames for each library in turn before giving up.
+
+=cut
+
+.sub _load_opengl_libs
+    .local pmc libgl, libglu, libglut, libglutcb
+    .local pmc libnci_test
+    .local pmc libnames
+
+    libnames = new 'ResizableStringArray'
+    push libnames, 'libGL'
+    libgl = load_lib_with_fallbacks('GL', libnames)
+    set_global 'libgl', libgl
+
+    libnames = new 'ResizableStringArray'
+    push libnames, 'libGLU'
+    libglu = load_lib_with_fallbacks('GLU', libnames)
+    set_global 'libglu', libglu
+
+    libnames = new 'ResizableStringArray'
+    push libnames, 'libglut'
+    libglut = load_lib_with_fallbacks('GLUT', libnames)
+    set_global 'libglut', libglut
+
+    libnames = new 'ResizableStringArray'
+    push libnames, 'libglutcb'
+    libglutcb = load_lib_with_fallbacks('GLUTCB', libnames)
+    set_global 'libglutcb', libglutcb
+.end
+
+
+=item load_lib_with_fallbacks(string friendly_name, pmc fallback_list)
+
+This function is more generally useful than just for this module -- it
+implements the search for a particular libary that may appear under any
+of several different filenames.  The C<fallback_list> should be a simple
+array of strings, each naming one of the possible filenames, I<without>
+the trailing shared library extension (e.g. C<.dll> or C<.so>).  The
+C<friendly_name> is only used to fill in the error message in case no
+match can be found on the system.
+
+=cut
+
+.sub load_lib_with_fallbacks
+    .param string friendly_name
+    .param pmc    fallback_list
+
+    .local pmc    list_iter
+    list_iter = new 'Iterator', fallback_list
+    list_iter = .ITERATE_FROM_START
+
+    .local string libname
+    .local pmc    library
+  iter_loop:
+    unless list_iter goto failed
+    libname = shift list_iter
+    library = loadlib libname
+    unless library goto iter_loop
+
+  loaded:
+    .return (library)
+
+  failed:
+    .local string message
+    message  = 'Could not find a suitable '
+    message .= friendly_name
+    message .= ' shared library!'
+
+    .local pmc    exception
+    exception = new 'Exception'
+    exception['_message'] = message
+    throw exception
+.end
+
+
+=item _wrap_all_opengl_entry_points()
+
+Create NCI wrappers for all GL, GLU, and GLUT functions
+
+=cut
+
+.sub _wrap_all_opengl_entry_points
+    .local pmc namespace
+    namespace = get_namespace
+
+    .local pmc libgl, libglu, libglut, libglutcb
+    libgl     = get_global 'libgl'
+    libglu    = get_global 'libglu'
+    libglut   = get_global 'libglut'
+    libglutcb = get_global 'libglutcb'
+
+    .local pmc gl_funcs, glu_funcs, glut_funcs, glutcb_funcs
+    gl_funcs     = gl_func_list()
+    glu_funcs    = glu_func_list()
+    glut_funcs   = glut_func_list()
+    glutcb_funcs = glutcb_func_list()
+
+    wrap_nci_list(namespace, libgl,     gl_funcs)
+    wrap_nci_list(namespace, libglu,    glu_funcs)
+    wrap_nci_list(namespace, libglut,   glut_funcs)
+    wrap_nci_list(namespace, libglutcb, glutcb_funcs)
+.end
+
+.sub gl_func_list
+    .local pmc gl_funcs
+    gl_funcs = new 'ResizableStringArray'
+    push gl_funcs, 'glBegin'
+    push gl_funcs, 'vi'
+    push gl_funcs, 'glClear'
+    push gl_funcs, 'vi'
+    push gl_funcs, 'glColor3f'
+    push gl_funcs, 'vfff'
+    push gl_funcs, 'glEnd'
+    push gl_funcs, 'v'
+    push gl_funcs, 'glFlush'
+    push gl_funcs, 'v'
+    push gl_funcs, 'glVertex3f'
+    push gl_funcs, 'vfff'
+    push gl_funcs, 'glRotatef'
+    push gl_funcs, 'vffff'
+
+    .return (gl_funcs)
+.end
+
+.sub glu_func_list
+    .local pmc glu_funcs
+    glu_funcs = new 'ResizableStringArray'
+
+    .return (glu_funcs)
+.end
+
+.sub glut_func_list
+    .local pmc glut_funcs
+    glut_funcs = new 'ResizableStringArray'
+    push glut_funcs, 'glutInit'
+    push glut_funcs, 'v3p'
+    push glut_funcs, 'glutInitDisplayMode'
+    push glut_funcs, 'vi'
+    push glut_funcs, 'glutCreateWindow'
+    push glut_funcs, 'it'
+    push glut_funcs, 'glutDestroyWindow'
+    push glut_funcs, 'vi'
+    push glut_funcs, 'glutMainLoop'
+    push glut_funcs, 'v'
+    push glut_funcs, 'glutPostRedisplay'
+    push glut_funcs, 'v'
+    push glut_funcs, 'glutSwapBuffers'
+    push glut_funcs, 'v'
+
+    .return (glut_funcs)
+.end
+
+.sub glutcb_func_list
+    .local pmc glutcb_funcs
+    glutcb_funcs = new 'ResizableStringArray'
+    push glutcb_funcs, 'glutcbCloseFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbDisplayFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbIdleFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbMenuDestroyFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbOverlayDisplayFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbWMCloseFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbEntryFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbMenuStateFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbVisibilityFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbWindowStatusFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbButtonBoxFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbDialsFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbMotionFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbPassiveMotionFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbReshapeFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbSpaceballButtonFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbTabletMotionFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbKeyboardFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbKeyboardUpFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbMenuStatusFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbSpaceballMotionFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbSpaceballRotateFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbSpecialFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbSpecialUpFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbMouseFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbMouseWheelFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbTabletButtonFunc'
+    push glutcb_funcs, 'vPP'
+    push glutcb_funcs, 'glutcbTimerFunc'
+    push glutcb_funcs, 'vPPii'
+    push glutcb_funcs, 'glutcbJoystickFunc'
+    push glutcb_funcs, 'vPPi'
+
+    .return (glutcb_funcs)
+.end
+
+
+=item wrap_nci_list(pmc namespace, pmc library, pmc nci_list)
+
+Create NCI wrappers for every C<library> entry point in C<nci_list>,
+and store the results in C<namespace> .  The list should consist of
+alternating function names and Parrot NCI signatures.
+
+=cut
+
+.sub wrap_nci_list
+    .param pmc namespace
+    .param pmc library
+    .param pmc nci_list
+
+    .local pmc namespace_key
+    namespace_key = namespace.get_name()
+
+    .local pmc list_iter
+    list_iter = new 'Iterator', nci_list
+    list_iter = .ITERATE_FROM_START
+
+    .local string func_name, signature
+    .local pmc    function
+  list_loop:
+    unless list_iter goto done
+    func_name = shift list_iter
+    signature = shift list_iter
+    function  = dlfunc library, func_name, signature
+    set_root_global namespace_key, func_name, function
+    goto list_loop
+
+  done:
+.end
+
+
+# Local Variables:
+#   mode: pir
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4 ft=pir:
=== src/call_list.txt
==================================================================
--- src/call_list.txt	(revision 4780)
+++ src/call_list.txt	(local)
@@ -278,6 +278,14 @@
 p    ptp
 i    pt33
 
+# Used by OpenGL (including GLU and GLUT)
+v    3p
+v    PP
+v    PPi
+v    PPii
+v    fff
+v    ffff
+
 # For python
 i    JPP
 
=== t/steps/auto_opengl-01.t
==================================================================
--- t/steps/auto_opengl-01.t	(revision 4780)
+++ t/steps/auto_opengl-01.t	(local)
@@ -0,0 +1,50 @@
+#! perl
+# Copyright (C) 2007-2008, The Perl Foundation.
+# $Id: $
+# auto_opengl-01.t
+
+use strict;
+use warnings;
+use Test::More tests =>  2;
+use Carp;
+use lib qw( lib );
+use_ok('config::auto::opengl');
+
+=for hints_for_testing This is just a stub so that Configure.pl will run.
+
+=cut
+
+pass("Completed all tests in $0");
+
+################### DOCUMENTATION ###################
+
+=head1 NAME
+
+  auto_opengl-01.t - test config::auto::opengl
+
+=head1 SYNOPSIS
+
+    % prove t/steps/auto_opengl-01.t
+
+=head1 DESCRIPTION
+
+The files in this directory test functionality used by F<Configure.pl>.
+
+The tests in this file test subroutines exported by config::auto::opengl.
+
+=head1 AUTHOR
+
+Geoffrey Broadwell; modified from a similar file by James E Keenan
+
+=head1 SEE ALSO
+
+config::auto::opengl, F<Configure.pl>.
+
+=cut
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
=== t/steps/gen_opengl-01.t
==================================================================
--- t/steps/gen_opengl-01.t	(revision 4780)
+++ t/steps/gen_opengl-01.t	(local)
@@ -0,0 +1,50 @@
+#! perl
+# Copyright (C) 2007-2008, The Perl Foundation.
+# $Id: $
+# gen_opengl-01.t
+
+use strict;
+use warnings;
+use Test::More tests =>  2;
+use Carp;
+use lib qw( lib );
+use_ok('config::gen::opengl');
+
+=for hints_for_testing This is just a stub so that Configure.pl will run.
+
+=cut
+
+pass("Completed all tests in $0");
+
+################### DOCUMENTATION ###################
+
+=head1 NAME
+
+  gen_opengl-01.t - test config::gen::opengl
+
+=head1 SYNOPSIS
+
+    % prove t/steps/gen_opengl-01.t
+
+=head1 DESCRIPTION
+
+The files in this directory test functionality used by F<Configure.pl>.
+
+The tests in this file test subroutines exported by config::gen::opengl.
+
+=head1 AUTHOR
+
+Geoffrey Broadwell; modified from a similar file by James E Keenan
+
+=head1 SEE ALSO
+
+config::gen::opengl, F<Configure.pl>.
+
+=cut
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:

Reply via email to