/* Last saved: Fri 16 Oct 2009 10:11:21 AM */ /* Copyright (c) 1998 Kenneth Albanowski. All rights reserved. * Copyright (c) 2007 Bob Free. All rights reserved. * Copyright (c) 2009 Chris Marshall. All rights reserved. * This program is free software; you can redistribute it and/or * modify it under the same terms as Perl itself. */ /* OpenGL *GLUT bindings */ #define IN_POGL_GLUT_XS #include #include "pgopogl.h" #ifdef HAVE_GL #include "gl_util.h" #endif #ifdef HAVE_GLX #include "glx_util.h" #endif #ifdef HAVE_GLU #include "glu_util.h" #endif #ifdef IN_POGL_GLUT_XS #if defined(HAVE_GLUT) || defined(HAVE_FREEGLUT) #ifndef GLUT_API_VERSION #define GLUT_API_VERSION 4 #endif #include "glut_util.h" #endif static int _done_glutInit = 0; #endif /* End IN_POGL_GLUT_XS */ /* This does not seem to be used */ #if 0 static char *SWIZZLE[4] = {"x","y","z","w"}; */ #endif /* This does not seem to be used */ #if 0 static int not_here(s) char *s; { croak("%s not implemented on this architecture", s); return -1; } #endif #ifdef IN_POGL_GLUT_XS #ifndef __PM__ # define DO_perl_call_sv(handler, flag) perl_call_sv(handler, flag) # define ENSURE_callback_thread # define GLUT_PUSH_NEW_SV(sv) XPUSHs(sv_2mortal(newSVsv(sv))) # define GLUT_PUSH_NEW_IV(i) XPUSHs(sv_2mortal(newSViv(i))) # define GLUT_PUSH_NEW_U8(c) XPUSHs(sv_2mortal(newSViv((int)c))) # define GLUT_EXTEND_STACK(sp,n) # define GLUT_PUSHMARK(sp) PUSHMARK(sp) #else /* GLUT on OS/2 PM runs callbacks from a secondary thread. This thread is not instrumented to run EMX CRTL functions. Basically, no Perl function may be run from this thread. We create a ternary thread via CRTL _beginthread() call, and communicate the requests to this thread via inter-thread communication (ITC). */ # define GLUT_PUSHMARK(sp) # include "sys/builtin.h" # include "sys/fmutex.h" # include "os2pm_X.h" # define DO_perl_call_sv(handler, flag) \ STMT_START { PUSHs(handler); \ PUTBACK; \ extend_by = 0; \ RUN_perl_call_sv(); \ } STMT_END # define GLUT_START_PUSHING 7 # define GLUT_PUSHING_IVP 17 # define GLUT_PUSHING_U8 27 # define GLUT_PUSHING_SV 37 # define GLUT_EXTEND_STACK(p,n) \ STMT_START { if (PL_stack_max - p < 2*(n)+4) { \ extend_by = 2*(n)+4; \ RUN_perl_call_sv(); \ } \ SPAGAIN; \ PUSHs((SV*)GLUT_START_PUSHING); \ } STMT_END # define GLUT_PUSH_NEW_SV(sv) (PUSHs(sv), PUSHs((SV*)GLUT_PUSHING_SV)) # define GLUT_PUSH_NEW_IV(i) (PUSHs((SV*)&i), PUSHs((SV*)GLUT_PUSHING_IVP)) # define GLUT_PUSH_NEW_U8(c) (PUSHs((SV*)(int)c), PUSHs((SV*)GLUT_PUSHING_U8)) _fmutex run_mutex, result_mutex; static int worker_started; static int extend_by; void RUN_perl_call_sv(void) { char *s = NULL; if (_fmutex_release(&run_mutex)) s = "Error unlocking the callback thread"; /* result_mutex is requested on entry! Block until looper finishes. */ else if (_fmutex_request(&result_mutex, _FMR_IGNINT)) s = "Error requesting the callback thread"; if (s) write(2, s, strlen(s)); return; } /* Main event handler for callbacks */ void callback_thread_looper(void *dummy) { while (1) { /* It is requested already! Wait until somebody requests a run */ if (_fmutex_request(&run_mutex, _FMR_IGNINT)) { warn("Error unlocking in the callback thread"); worker_started = 0; return; } if (extend_by) { /* Need to extend the stack */ dSP; EXTEND(sp, extend_by); } else { dSP; SV* handler = POPs; STRLEN n_a; SV **last = sp, **f; /* The rest is put on stack in a "raw" pointer form */ while (1) { switch ((IV)*sp) { case GLUT_START_PUSHING: goto start_found; break; case GLUT_PUSHING_IVP: case GLUT_PUSHING_SV: case GLUT_PUSHING_U8: break; default: croak("Panic: broken descriptor/down when ITC for Glut: %#lx", (unsigned long)*sp); break; } sp -= 2; } start_found: f = sp + 1; sp--; PUSHMARK(sp); while (f < last) { switch ((IV)f[1]) { case GLUT_PUSHING_IVP: PUSHs(sv_2mortal(newSViv(*(IV*)*f))); break; case GLUT_PUSHING_U8: PUSHs(sv_2mortal(newSViv((IV)*f))); break; case GLUT_PUSHING_SV: PUSHs(sv_2mortal(newSVsv(*f))); break; default: croak("Panic: broken descriptor/up when ITC for Glut: %#lx", (unsigned long)f[1]); break; } f += 2; } PUTBACK; perl_call_sv(handler, G_DISCARD|G_EVAL); if (SvTRUE(ERRSV)) fprintf(stderr, "Error in a GLUT Callback: %s", SvPV(ERRSV, n_a)); } if (_fmutex_release(&result_mutex)) { warn("Error in a callback thread"); worker_started = 0; return; } } } # define ENSURE_callback_thread \ if (!worker_started) \ start_callback_thread() /* Spin a thread for a callback */ void start_callback_thread() { unsigned long rc; if (worker_started) return; if ( CheckOSError(_fmutex_create(&run_mutex, 0)) || CheckOSError(_fmutex_create(&result_mutex, 0)) || CheckOSError(_fmutex_request(&run_mutex, _FMR_IGNINT)) || CheckOSError(_fmutex_request(&result_mutex, _FMR_IGNINT))) croak("Error creating semaphores"); worker_started = _beginthread(&callback_thread_looper, NULL, 8*1024*1024, NULL); if (worker_started == -1) { worker_started = 0; croak("Error creating callback thread"); } } #endif /* __PM__ */ #endif /* End IN_POGL_GLUT_XS */ /* #define PackCallbackST(av,first) \ if (SvROK(ST(first)) && (SvTYPE(SvRV(ST(first))) == SVt_PVAV)){ \ int i; \ AV * x = (AV*)SvRV(ST(first)); \ for(i=0;i<=av_len(x);i++) { \ av_push(av, newSVsv(*av_fetch(x, i, 0))); \ } \ } else { \ int i; \ for(i=first;i= 3 #//# glutFullScreen(); void glutFullScreen() #endif #//# glutPopWindow(); void glutPopWindow() #//# glutPushWindow(); void glutPushWindow() #//# glutShowWindow(); void glutShowWindow() #//# glutHideWindow(); void glutHideWindow() #//# glutIconifyWindow(); void glutIconifyWindow() #//# glutSetWindowTitle($title); void glutSetWindowTitle(title) char * title #//# glutSetIconTitle($title); void glutSetIconTitle(title) char * title #if GLUT_API_VERSION >= 3 #//# glutSetCursor(cursor); void glutSetCursor(cursor) int cursor #endif # Overlays #if GLUT_API_VERSION >= 3 #//# glutEstablishOverlay(); void glutEstablishOverlay() #//# glutUseLayer(layer); void glutUseLayer(layer) GLenum layer #//# glutRemoveOverlay(); void glutRemoveOverlay() #//# glutPostOverlayRedisplay(); void glutPostOverlayRedisplay() #//# glutShowOverlay(); void glutShowOverlay() #//# glutHideOverlay(); void glutHideOverlay() #endif # Menus #//# $ID = glutCreateMenu(\&callback); int glutCreateMenu(handler=0, ...) SV * handler CODE: { if (!handler || !SvOK(handler)) { croak("A handler must be specified"); } else { AV * handler_data = newAV(); PackCallbackST(handler_data, 0); RETVAL = glutCreateMenu(generic_glut_menu_handler); if (!glut_menu_handlers) glut_menu_handlers = newAV(); av_store(glut_menu_handlers, RETVAL, newRV_inc((SV*)handler_data)); SvREFCNT_dec(handler_data); } } OUTPUT: RETVAL #//# glutSetMenu($menu); void glutSetMenu(menu) int menu #//# glutGetMenu(); int glutGetMenu() #//# glutDestroyMenu($menu); void glutDestroyMenu(menu) int menu CODE: { glutDestroyMenu(menu); av_store(glut_menu_handlers, menu, newSVsv(&PL_sv_undef)); } #//# glutAddMenuEntry($name, $value); void glutAddMenuEntry(name, value) char * name int value #//# glutAddSubMenu($name, $menu); void glutAddSubMenu(name, menu) char * name int menu #//# glutChangeToMenuEntry($entry, $name, $value); void glutChangeToMenuEntry(entry, name, value) int entry char * name int value #//# glutChangeToSubMenu($entry, $name, $menu); void glutChangeToSubMenu(entry, name, menu) int entry char * name int menu #//# glutRemoveMenuItem($entry); void glutRemoveMenuItem(entry) int entry #//# glutAttachMenu(button); void glutAttachMenu(button) int button #//# glutDetachMenu(button); void glutDetachMenu(button) int button # Callbacks #//# glutDisplayFunc(\&callback); void glutDisplayFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs_nullfail(Display, ("Display function must be specified")) #if GLUT_API_VERSION >= 3 #//# glutOverlayDisplayFunc(\&callback); void glutOverlayDisplayFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(OverlayDisplay) #endif #//# glutReshapeFunc(\&callback); void glutReshapeFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Reshape) #//# glutKeyboardFunc(\&callback); void glutKeyboardFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Keyboard) #if GLUT_API_VERSION >= 4 #//# glutKeyboardUpFunc(\&callback); void glutKeyboardUpFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(KeyboardUp) #//# glutWindowStatusFunc(\&callback); void glutWindowStatusFunc(handler=0, ...) SV * handler CODE: { #if defined HAVE_FREEGLUT decl_gwh_xs(WindowStatus) #endif } #endif #//# glutMouseFunc(\&callback); void glutMouseFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Mouse) #//# glutMouseWheelFunc(\&callback); void glutMouseWheelFunc(handler=0, ...) SV * handler CODE: { #if defined HAVE_FREEGLUT decl_gwh_xs(MouseWheel) #endif } #//# glutMotionFunc(\&callback); void glutMotionFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Motion) #//# glutPassiveMotionFunc(\&callback); void glutPassiveMotionFunc(handler=0, ...) SV * handler CODE: { #if defined HAVE_FREEGLUT decl_gwh_xs(PassiveMotion) #endif } #//# glutVisibilityFunc(\&callback); void glutVisibilityFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Visibility) # OS/2 PM implementation calls itself v2, but does not support these functions # It is very hard to test for this, so we check for some other omission... #if !defined(GL_SRC_ALPHA_SATURATE) || defined(GL_CONSTANT_COLOR) #//# glutEntryFunc(\&callback); void glutEntryFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Entry) #endif #if GLUT_API_VERSION >= 2 #//# glutSpecialFunc(\&callback); void glutSpecialFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Special) # OS/2 PM implementation calls itself v2, but does not support these functions # It is very hard to test for this, so we check for some other omission... # if !defined(GL_SRC_ALPHA_SATURATE) || defined(GL_CONSTANT_COLOR) #//# glutJoystickFunc(\&callback); /* Open/FreeGLUT -chm */ # void /* Not implemented, don't know how */ # glutJoystickFunc(handler=0, ...) # SV * handler # CODE: # decl_gwh_xs(Joystick) #//# glutSpaceballMotionFunc(\&callback); void glutSpaceballMotionFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(SpaceballMotion) #//# glutSpaceballRotateFunc(\&callback); void glutSpaceballRotateFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(SpaceballRotate) #//# glutSpaceballButtonFunc(\&callback); void glutSpaceballButtonFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(SpaceballButton) #//# glutButtonBoxFunc(\&callback); void glutButtonBoxFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(ButtonBox) #//# glutDialsFunc(\&callback); void glutDialsFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(Dials) #//# glutTabletMotionFunc(\&callback); void glutTabletMotionFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(TabletMotion) #//# glutTabletButtonFunc(\&callback); void glutTabletButtonFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(TabletButton) # endif #endif #if GLUT_API_VERSION >= 3 #//# glutMenuStatusFunc(\&callback); void glutMenuStatusFunc(handler=0, ...) SV * handler CODE: decl_ggh_xs(MenuStatus) #endif #//# glutMenuStateFunc(\&callback); void glutMenuStateFunc(handler=0, ...) SV * handler CODE: { #if defined HAVE_FREEGLUT decl_ggh_xs(MenuState) #endif } #//# glutIdleFunc(\&callback); void glutIdleFunc(handler=0, ...) SV * handler CODE: decl_ggh_xs(Idle) #//# glutTimerFunc($msecs, \&callback); void glutTimerFunc(msecs, handler=0, ...) unsigned int msecs SV * handler CODE: { if (!handler || !SvOK(handler)) { croak("A handler must be specified"); } else { AV * handler_data = newAV(); PackCallbackST(handler_data, 1); glutTimerFunc(msecs, generic_glut_timer_handler, (int)handler_data); } ENSURE_callback_thread;} # Colors #//# glutSetColor($cell, $red, $green, $blue) void glutSetColor(cell, red, green, blue) int cell GLfloat red GLfloat green GLfloat blue #//# glutGetColor($cell, $component); GLfloat glutGetColor(cell, component) int cell int component #//# glutCopyColormap($win); void glutCopyColormap(win) int win # State #//# glutGet($state); int glutGet(state) GLenum state #if GLUT_API_VERSION >= 3 #//# glutLayerGet(info); int glutLayerGet(info) GLenum info #endif int glutDeviceGet(info) GLenum info #if GLUT_API_VERSION >= 3 #//# glutGetModifiers(); int glutGetModifiers() #endif #if GLUT_API_VERSION >= 2 #//# glutExtensionSupported($extension); int glutExtensionSupported(extension) char * extension #endif # Font #//# glutBitmapCharacter($font, $character); void glutBitmapCharacter(font, character) void * font int character #//# glutStrokeCharacter($font, $character); void glutStrokeCharacter(font, character) void * font int character # OS/2 PM implementation calls itself v2, but does not support these functions # It is very hard to test for this, so we check for some other omission... #if GLUT_API_VERSION >= 2 && (!defined(GL_SRC_ALPHA_SATURATE) || defined(GL_CONSTANT_COLOR)) #//# glutBitmapWidth($font, $character); int glutBitmapWidth(font, character) void * font int character #//# glutStrokeWidth($font, $character); int glutStrokeWidth(font, character) void * font int character #endif #if GLUT_API_VERSION >= 3 #//# glutIgnoreKeyRepeat($ignore); void glutIgnoreKeyRepeat(ignore) int ignore #//# glutSetKeyRepeat($repeatMode); void glutSetKeyRepeat(repeatMode) int repeatMode #//# glutForceJoystickFunc(); void glutForceJoystickFunc() #endif # Solids #//# glutSolidSphere($radius, $slices, $stacks); void glutSolidSphere(radius, slices, stacks) GLdouble radius GLint slices GLint stacks #//# glutWireSphere($radius, $slices, $stacks); void glutWireSphere(radius, slices, stacks) GLdouble radius GLint slices GLint stacks #//# glutSolidCube($size); void glutSolidCube(size) GLdouble size #//# glutWireCube($size); void glutWireCube(size) GLdouble size #//# glutSolidCone($base, $height, $slices, $stacks); void glutSolidCone(base, height, slices, stacks) GLdouble base GLdouble height GLint slices GLint stacks #//# glutWireCone($base, $height, $slices, $stacks); void glutWireCone(base, height, slices, stacks) GLdouble base GLdouble height GLint slices GLint stacks #//# glutSolidTorus($innerRadius, $outerRadius, $nsides, $rings); void glutSolidTorus(innerRadius, outerRadius, nsides, rings) GLdouble innerRadius GLdouble outerRadius GLint nsides GLint rings #//# glutWireTorus($innerRadius, $outerRadius, $nsides, $rings); void glutWireTorus(innerRadius, outerRadius, nsides, rings) GLdouble innerRadius GLdouble outerRadius GLint nsides GLint rings #//# glutSolidDodecahedron(); void glutSolidDodecahedron() #//# glutWireDodecahedron(); void glutWireDodecahedron() #//# glutSolidOctahedron(); void glutSolidOctahedron() #//# glutWireOctahedron(); void glutWireOctahedron() #//# glutSolidTetrahedron(); void glutSolidTetrahedron() #//# glutWireTetrahedron(); void glutWireTetrahedron() #//# glutSolidIcosahedron(); void glutSolidIcosahedron() #//# glutWireIcosahedron(); void glutWireIcosahedron() #//# glutSolidTeapot(size); void glutSolidTeapot(size) GLdouble size #//# glutWireTeapot($size); void glutWireTeapot(size) GLdouble size #if GLUT_API_VERSION >= 4 #//# glutSpecialUpFunc(\&callback); void glutSpecialUpFunc(handler=0, ...) SV * handler CODE: decl_gwh_xs(SpecialUp) #//# glutGameModeString($string); GLboolean glutGameModeString(string) char * string CODE: { char mode[1024]; if (!string || !string[0]) { int w = glutGet(0x00C8); // GLUT_SCREEN_WIDTH int h = glutGet(0x00C9); // GLUT_SCREEN_HEIGHT sprintf(mode,"%dx%d:%d@%d",w,h,32,60); string = mode; } glutGameModeString(string); RETVAL = glutGameModeGet(0x0001); // GLUT_GAME_MODE_POSSIBLE } OUTPUT: RETVAL #//# glutEnterGameMode(); int glutEnterGameMode() #//# glutLeaveGameMode(); void glutLeaveGameMode() #//# glutGameModeGet($mode); int glutGameModeGet(mode) GLenum mode #//# FreeGLUT/OpenGLUT feature #//# int glutBitmapHeight (void *font) int glutBitmapHeight(font) void * font CODE: { #if defined HAVE_FREEGLUT RETVAL = glutBitmapHeight(font); #endif } #//# FreeGLUT/OpenGLUT feature #//# int glutBitmapLength (void *font, const unsigned char *string) int glutBitmapLength(font, string) void * font const unsigned char * string CODE: { #if defined HAVE_FREEGLUT RETVAL = glutBitmapLength(font, string); #endif } #//# FreeGLUT/OpenGLUT feature #//# void glutBitmapString (void *font, const unsigned char *string) void glutBitmapString(font, string) void * font const unsigned char * string CODE: { #if defined HAVE_FREEGLUT glutBitmapString(font, string); #else int len, i; len = (int) strlen(string); for (i = 0; i < len; i++) { glutBitmapCharacter(font, string[i]); } #endif } #//# FreeGLUT/OpenGLUT feature #//# void * glutGetProcAddress (const char *procName) # void * # glutGetProcAddress(procName) # const char * procName #//# FreeGLUT/OpenGLUT feature #//# void glutMainLoopEvent (void) void glutMainLoopEvent() CODE: { #if defined HAVE_AGL_GLUT glutCheckLoop(); #elif defined HAVE_FREEGLUT glutMainLoopEvent(); #endif } #//# void glutPostWindowOverlayRedisplay (int windowID) void glutPostWindowOverlayRedisplay(windowID) int windowID #//# void glutPostWindowRedisplay (int windowID) void glutPostWindowRedisplay(windowID) int windowID #//# void glutReportErrors (void) void glutReportErrors() #//# void glutSolidCylinder (GLdouble radius, GLdouble height, GLint slices, GLint stacks) void glutSolidCylinder(radius, height, slices, stacks) GLdouble radius GLdouble height GLint slices GLint stacks CODE: { #if defined HAVE_FREEGLUT glutSolidCylinder(radius, height, slices, stacks); #endif } #//# void glutSolidRhombicDodecahedron (void) void glutSolidRhombicDodecahedron() CODE: { #if defined HAVE_FREEGLUT glutSolidRhombicDodecahedron(); #endif } #//# float glutStrokeHeight (void *font) GLfloat glutStrokeHeight(font) void * font CODE: { #if defined HAVE_FREEGLUT RETVAL = glutStrokeHeight(font); #endif } #//# float glutStrokeLength (void *font, const unsigned char *string) GLfloat glutStrokeLength(font, string) void * font const unsigned char * string #//# void glutStrokeString (void *fontID, const unsigned char *string) void glutStrokeString(font, string) void * font const unsigned char * string CODE: { #if defined HAVE_FREEGLUT glutStrokeString(font, string); #endif } #//# void glutWarpPointer (int x, int y) void glutWarpPointer(x, y) int x int y #//# void glutWireCylinder (GLdouble radius, GLdouble height, GLint slices, GLint stacks) void glutWireCylinder(radius, height, slices, stacks) GLdouble radius GLdouble height GLint slices GLint stacks CODE: { #if defined HAVE_FREEGLUT glutWireCylinder(radius, height, slices, stacks); #endif } #//# void glutWireRhombicDodecahedron (void) void glutWireRhombicDodecahedron() CODE: { #if defined HAVE_FREEGLUT glutWireRhombicDodecahedron(); #endif } #endif # /* FreeGLUT APIs */ #//# glutSetOption($option_flag, $value); void glutSetOption(option_flag, value) GLenum option_flag int value CODE: { #if defined HAVE_FREEGLUT glutSetOption(option_flag, value); #endif } #//# glutLeaveMainLoop(); void glutLeaveMainLoop() CODE: { #if defined HAVE_FREEGLUT glutLeaveMainLoop(); #else int win = glutGetWindow(); glutDestroyWindow(win); destroy_glut_win_handlers(win); #endif } #//# glutMenuDestroyFunc(\&callback); void glutMenuDestroyFunc(handler=0, ...) SV * handler CODE: { #if defined HAVE_FREEGLUT decl_gwh_xs(MenuDestroy) #endif } #//# glutCloseFunc(\&callback); void glutCloseFunc(handler=0, ...) SV * handler CODE: { #if defined HAVE_FREEGLUT decl_gwh_xs(Close) #elif defined HAVE_AGL_GLUT decl_gwh_xs(WMClose) #endif } #endif /* def GLUT_API_VERSION */ #endif /* End IN_POGL_GLUT_XS */ # /* This is assigned to GLX for now. The glp*() functions should be split out */