#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #ifndef PERL_DARWIN #include #else #include #endif #include "const-c.inc" HV *TimerFuncs = NULL; AV *MenuFuncs = NULL; /* * There now follows some CPP macrology in order to reduce cut-and-paste * coding to generate the code to handle GLUT callbacks. * * Most glutFooFunc() callback-setting routines, with the notable * exception of glutTimerFunc(), take a single argument: a pointer to * the callback function. This function will take a number of arguments * which depends on precisely which callback it is handling. * * However, most of these callbacks are not global: they are per-window. * The way this is handled in XS is as follows: take glutDisplayFunc as * an example. * * Define an AV, DisplayFuncs. If glutDisplayFunc() is called, the id * of the current window is found from glutGetWindow(); this will be a * small integer. This integer is used as an index into DisplayFuncs, * and the coderef passed to glutDisplayFuncs is stored at this index. * * Now, define the function void DisplayCallback(void) , which finds * the current window ID, and then calls the appropriate coderef stored * in DisplayFuncs. Set *this* function to be the callback, as far as * GLUT is concerned. * * Since there are many callbacks of many types, this procedure has * been automated using cpp. The register_callback() macro defines the * appropriate AV and FooCallback() function; it takes some code as an * argument to set up the perl argument stack. * * This is in turn called from the macros register_callback_noargs(), * register_callback_2i(), etc. These take one argument, the callback ID * (such as "Display" to define DisplayFuncs, DisplayCallback() etc), * and make the appropriate calls to register_callback to define a * callback function which takes no arguments, two integer arguments, * etc. * * To summarize, say you have a new kind of callback, Foo, which is called * with two integer arguments. To set this up in XS, you do: * * (1) in the plain-C section: * * register_callback_2i(Foo) * * (2) in the XS section: * * void glutFooFunc(SV *cb) * CODE: * register_callback_xs(Foo) * * That's it! * * */ #define register_callback(cbname,prototype,xscode,callflags) \ AV * cbname ## Funcs=NULL; \ void cbname ## Callback prototype \ { \ int id; \ SV **fetchval; \ \ dSP; \ id = glutGetWindow(); \ if (NULL== cbname ## Funcs) { \ croak(#cbname "Callback called with id=%d but " \ #cbname "Funcs is NULL!", id); \ } \ \ if (NULL==(fetchval=av_fetch( cbname ## Funcs,id,FALSE))) { \ croak("No " #cbname "Func defined for id=%d",id); \ } \ \ ENTER; \ SAVETMPS; \ PUSHMARK(SP); \ xscode \ \ PUTBACK; \ \ call_sv((SV *)*fetchval,G_DISCARD|callflags); \ \ FREETMPS; \ LEAVE; \ \ \ return; \ \ } #define register_callback_noargs(cbname) \ register_callback(cbname,(void),,G_NOARGS) #define register_callback_1i(cbname) \ register_callback(cbname,(int a), \ XPUSHs(sv_2mortal(newSViv(a))); ,0)\ #define register_callback_2i(cbname) \ register_callback(cbname,(int a, int b), \ XPUSHs(sv_2mortal(newSViv(a))); \ XPUSHs(sv_2mortal(newSViv(b))); ,0)\ #define register_callback_3i(cbname) \ register_callback(cbname,(int a, int b, int c), \ XPUSHs(sv_2mortal(newSViv(a))); \ XPUSHs(sv_2mortal(newSViv(b))); \ XPUSHs(sv_2mortal(newSViv(c))); ,0)\ #define register_callback_4i(cbname) \ register_callback(cbname,(int a, int b, int c, int d), \ XPUSHs(sv_2mortal(newSViv(a))); \ XPUSHs(sv_2mortal(newSViv(b))); \ XPUSHs(sv_2mortal(newSViv(c))); \ XPUSHs(sv_2mortal(newSViv(d))); ,0)\ #define register_callback_uc2i(cbname) \ register_callback(cbname,(unsigned char uc, int a, int b), \ XPUSHs(sv_2mortal(newSVuv(uc))); \ XPUSHs(sv_2mortal(newSViv(a))); \ XPUSHs(sv_2mortal(newSViv(b))); ,0)\ #define register_callback_xs(cbname) \ if (!SvROK(cb)) { \ croak("Callback must be code reference"); \ return; \ } \ if (SVt_PVCV != SvTYPE(SvRV(cb))) { \ croak("Callback must be code reference"); \ return; \ } \ \ if (NULL== cbname ## Funcs) { \ cbname ## Funcs = newAV(); \ } \ \ av_store( cbname ## Funcs,glutGetWindow(),newSVsv(cb)); \ glut ## cbname ## Func( cbname ## Callback); \ /*********** End of macro definitions *************/ /* Callback definitions */ register_callback_noargs(Display) register_callback_noargs(OverlayDisplay) register_callback_2i(Reshape) register_callback_uc2i(Keyboard) register_callback_4i(Mouse) register_callback_2i(Motion) register_callback_2i(PassiveMotion) register_callback_1i(Visibility) register_callback_1i(Entry) register_callback_3i(Special) register_callback_3i(SpaceballMotion) register_callback_3i(SpaceballRotate) register_callback_2i(SpaceballButton) register_callback_2i(ButtonBox) register_callback_2i(Dials) register_callback_2i(TabletMotion) register_callback_4i(TabletButton) register_callback_3i(MenuStatus) register_callback_1i(MenuState) register_callback_noargs(Idle) /* Plain C code */ void TimerCallback(int value) { HE *hashent; SV *hashvalue,*callback,*realvalue; AV *myarr; dSP; hashent = hv_fetch_ent(TimerFuncs,newSViv(value),FALSE,0); if (NULL==hashent) { croak("TimerCallback(value=%d) has no hash entry!",value); } hashvalue = HeVAL(hashent); /* * Be really anal about what we get back from the hash. * To start with, make sure we're holding a reference to an array. * */ if (!SvROK(hashvalue)) { croak("TimerCallback: hash entry is not a reference!"); return; } if (SVt_PVAV != SvTYPE(SvRV(hashvalue))) { croak("TimerCallback: hash entry is not an array reference!"); return; } myarr = (AV *)SvRV(hashvalue); if (1!=av_len(myarr)) { croak("TimerCallback: av_len(myarr)=%d not 1!\n",av_len(myarr)); } /* OK, finally grab the coderef and value out of the array */ callback = *av_fetch(myarr,0,FALSE); realvalue = *av_fetch(myarr,1,FALSE); /* Delete the hash entry now that we've finished with it */ hv_delete_ent(TimerFuncs,newSViv(value),G_DISCARD,0); ENTER; SAVETMPS; PUSHMARK(SP); /* Set up stack frame for routine we'll call */ XPUSHs(sv_2mortal(newSVsv(realvalue))); /* dump "value" arg on stack */ PUTBACK; call_sv((SV *)callback,G_DISCARD); /* Call callback */ FREETMPS; LEAVE; } void MenuCallback(int value) { int id; SV **fetchval; dSP; id = glutGetMenu(); if (NULL==MenuFuncs) { croak("MenuCallback called with id=%d but MenuFuncs is NULL!", id); } if (NULL==(fetchval=av_fetch(MenuFuncs,id,FALSE))) { croak("No MenuFunc defined for id=%d",id); } ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSViv(value))); PUTBACK; call_sv((SV *)*fetchval,G_DISCARD); FREETMPS; LEAVE; return; } MODULE = OpenGL::Simple::GLUT PACKAGE = OpenGL::Simple::GLUT INCLUDE: const-xs.inc # ###### Initialization void glutInit() PREINIT: int argc; int i; char **argv; AV *ARGV; SV *ARGV0; CODE: ARGV = perl_get_av("ARGV",FALSE); ARGV0= perl_get_sv("0",FALSE); argc = 2 + av_len(ARGV); if (NULL==(argv=malloc(sizeof(char *)*argc))) { perror("malloc()"); croak("malloc() failed"); } argv[0] = SvPV_nolen(ARGV0); for (i=0;i<(argc-1);i++) { argv[i+1] = SvPV_nolen(*av_fetch(ARGV,i,FALSE)); } glutInit(&argc,argv); free(argv); void glutInitWindowSize(int width, int height); void glutInitWindowPosition(int x, int y); void glutInitDisplayMode(unsigned int mode); # ######## Beginning Event Processing void glutMainLoop(); # ######## Window Management int glutCreateWindow(char *name); int glutCreateSubWindow(int win, int x, int y, int width, int height); void glutSetWindow(int win); int glutGetWindow(); void glutDestroyWindow(int win); void glutPostRedisplay(); void glutSwapBuffers(); void glutPositionWindow(int x, int y); void glutReshapeWindow(int width, int height); void glutFullScreen(); void glutPopWindow(); void glutPushWindow(); void glutShowWindow(); void glutHideWindow(); void glutIconifyWindow(); void glutSetWindowTitle(char *name); void glutSetIconTitle(char *name); void glutSetCursor(int cursor); # ######## Overlay Management void glutEstablishOverlay(); void glutUseLayer(GLenum layer) void glutRemoveOverlay(); void glutPostOverlayRedisplay(); void glutShowOverlay(); void glutHideOverlay(); # ######## Menu Management int glutCreateMenu (SV *cb) PREINIT: int newid; /* ID of newly created menu */ CODE: /* Make sure we've been passed a coderef */ if (!SvROK(cb)) { croak("Callback must be code reference"); return; } if (SVt_PVCV != SvTYPE(SvRV(cb))) { croak("Callback must be code reference"); return; } if (NULL== MenuFuncs) { MenuFuncs = newAV(); } if (0==(newid = glutCreateMenu(MenuCallback))) { croak("glutCreateMenu() failed"); } av_store( MenuFuncs,newid,newSVsv(cb)); RETVAL = newid; OUTPUT: RETVAL void glutSetMenu(int menu); int glutGetMenu(); void glutDestroyMenu(int menu); void glutAddMenuEntry(char *name, int value); void glutAddSubMenu(char *name, int menu); void glutChangeToMenuEntry(int entry, char *name, int value); void glutChangeToSubMenu(int entry, char *name, int menu); void glutRemoveMenuItem(int entry); void glutAttachMenu(int button); void glutDetachMenu(int button); # ######## Callback Registration. # Register a display callback. # There can be at most one display callback for each GLUT window. # We keep an array, DisplayFuncs, such that the Nth element of # Displayfuncs, if defined, contains a coderef for the Nth window # display callback. void glutDisplayFunc(SV *cb) CODE: register_callback_xs(Display) void glutOverlayDisplayFunc(SV *cb) CODE: register_callback_xs(OverlayDisplay) void glutReshapeFunc(SV *cb) CODE: register_callback_xs(Reshape) void glutKeyboardFunc(SV *cb) CODE: register_callback_xs(Keyboard) void glutMouseFunc(SV *cb) CODE: register_callback_xs(Mouse) void glutMotionFunc(SV *cb) CODE: register_callback_xs(Motion) void glutPassiveMotionFunc(SV *cb) CODE: register_callback_xs(PassiveMotion) void glutVisibilityFunc(SV *cb) CODE: register_callback_xs(Visibility) void glutEntryFunc(SV *cb) CODE: register_callback_xs(Entry) void glutSpecialFunc(SV *cb) CODE: register_callback_xs(Special) void glutSpaceballMotionFunc(SV *cb) CODE: register_callback_xs(SpaceballMotion) void glutSpaceballRotateFunc(SV *cb) CODE: register_callback_xs(SpaceballRotate) void glutSpaceballButtonFunc(SV *cb) CODE: register_callback_xs(SpaceballButton) void glutButtonBoxFunc(SV *cb) CODE: register_callback_xs(ButtonBox) void glutDialsFunc(SV *cb) CODE: register_callback_xs(Dials) void glutTabletMotionFunc(SV *cb) CODE: register_callback_xs(TabletMotion) void glutTabletButtonFunc(SV *cb) CODE: register_callback_xs(TabletButton) void glutMenuStatusFunc(SV *cb) CODE: register_callback_xs(MenuStatus) void glutMenuStateFunc(SV *cb) CODE: register_callback_xs(MenuState) void glutIdleFunc(SV *cb) CODE: register_callback_xs(Idle) # glutTimerFunc doesn't care about window IDs, and the callback can be # called at any time. However, the callback is passed an ID number ("value"), # which can be used to tell which callback is being called. # So, we actually create our own unique ID for each timer callback. # We use that as an index into the TimerFuncs hash, which gives back # an array containing: (1) the coderef for the callback, and (2) the value # which the user passed in. void glutTimerFunc(unsigned int msecs, SV *cb, int value) PREINIT: AV *myav; SV *myrv; int key; SV *keysv; int i; CODE: if (!SvROK(cb)) { croak("Callback must be code reference"); return; } if (SVt_PVCV != SvTYPE(SvRV(cb))) { croak("Callback must be code reference"); return; } /* Everything looks OK, so store it in the callback array */ if (NULL==TimerFuncs) { TimerFuncs = newHV(); } /* Create an anonymous array to hold the coderef and value */ myav = newAV(); av_store(myav,0,newSVsv(cb)); av_store(myav,1,newSViv(value)); myrv = newRV( (SV *)myav ); /* Reference to the array */ /* Generate a new ID. This algorithm sucks. */ key=0; keysv = newSViv(key); while (NULL != hv_fetch_ent(TimerFuncs,keysv,FALSE,0)) { key++; keysv = newSViv(key); } /* key is an unique integer ID, and keySV is an SV * containing it. * Store the array in the hash. Effectively, we've just done: * * sub timerfunc { * my ($coderef,$val) = @_; * my $i; * while (exists($TimerFuncs{$i})) { * $i++; * } * $TimerFuncs{$i} = [ $coderef, $val ]; * } * */ hv_store_ent(TimerFuncs,keysv,myrv,0); /* Now register our timer callback handler with glut. */ glutTimerFunc(msecs,TimerCallback,key); # ######## Color Index Colormap Management void glutSetColor(int cell, GLfloat red, GLfloat green, GLfloat blue); GLfloat glutGetColor(int cell, int component); void glutCopyColormap(int win); # ######## State Retrieval int glutGet(GLenum state); int glutLayerGet(GLenum info); int glutDeviceGet(GLenum info); int glutGetModifiers(); int glutExtensionSupported(char *extension); # ######## Geometric Object Rendering void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks); void glutWireSphere(GLdouble radius, GLint slices, GLint stacks); void glutSolidCube(GLdouble size); void glutWireCube(GLdouble size); void glutSolidCone(GLdouble base, GLdouble height, GLint slices, GLint stacks); void glutWireCone(GLdouble base, GLdouble height, GLint slices, GLint stacks); void glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings); void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings); void glutSolidDodecahedron(); void glutWireDodecahedron(); void glutSolidOctahedron(); void glutWireOctahedron(); void glutSolidTetrahedron(); void glutWireTetrahedron(); void glutSolidIcosahedron(); void glutWireIcosahedron(); void glutSolidTeapot(GLdouble size); void glutWireTeapot(GLdouble size);