/* $Id: togl.c,v 1.142 2009/12/23 21:50:49 gregcouch Exp $ */ /* vi:set sw=4 expandtab: */ /* * Togl - a Tk OpenGL widget * * Copyright (C) 1996-2002 Brian Paul and Ben Bederson * Copyright (C) 2005-2009 Greg Couch * See the LICENSE file for copyright details. */ /* * Currently we support X11, Win32 and Mac OS X only */ #ifndef MODULE_SCOPE # ifdef __cplusplus # define MODULE_SCOPE extern "C" # else # define MODULE_SCOPE extern # endif #endif #pragma clang diagnostic ignored "-Wdeprecated-declarations" #define USE_TOGL_STUB_PROCS #include "togl.h" #include // don't need it on osx ??? #include #ifndef TOGL_USE_FONTS # define TOGL_USE_FONTS 1 #endif #if (TK_MAJOR_VERSION > 8 || TK_MINOR_VERSION > 4) && !defined(TOGL_WGL) /* X11 and Aqua font technology changed in 8.5 */ # undef TOGL_USE_FONTS #endif #ifndef TOGL_USE_OVERLAY # if defined(TOGL_X11) || defined(TOGL_WGL) # define TOGL_USE_OVERLAY 1 # endif #endif /* Use TCL_STUPID to cast (const char *) to (char *) where the Tcl function * prototype argument should really be const */ #define TCL_STUPID (char *) /* Use WIDGREC to cast widgRec or recordPtr arguments */ #define WIDGREC (char *) /*** Windows headers ***/ #if defined(TOGL_WGL) # define WIN32_LEAN_AND_MEAN # include # undef WIN32_LEAN_AND_MEAN # include # ifndef PFD_SUPPORT_COMPOSITION // for Vista -- not strictly needed because we don't use PFD_SUPPORT_GDI/BITMAP # define PFD_SUPPORT_COMPOSITION 0x00008000 # endif # include # include # include # ifdef _MSC_VER # include # else # ifdef UNICODE # define StringCchPrintf snwprintf # else # define StringCchPrintf snprintf # endif # endif /*** X Window System headers ***/ #elif defined(TOGL_X11) # include # include # include /* for XA_RGB_DEFAULT_MAP atom */ # if !defined(USE_SYSTEM_XMU) # include "Xmu/StdCmap.h" # else # if defined(__vms) # include /* for XmuLookupStandardColormap */ # else # include /* for XmuLookupStandardColormap */ # endif # endif # define GLX_GLXEXT_LEGACY /* include glxext.h separately */ # include /* we want the prototype typedefs from glxext.h */ # undef GLX_VERSION_1_3 # undef GLX_VERSION_1_4 # ifdef UNDEF_GET_PROC_ADDRESS # undef GLX_ARB_get_proc_address # endif # include # ifdef __sgi # include # endif # ifdef HAVE_AUTOSTEREO # include # endif /*** Mac Carbon headers ***/ #elif defined(TOGL_AGL) # define Cursor QDCursor # include # undef Cursor # include /* usa MacDrawable */ # include # define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr) /*** Mac Cocoa headers ***/ #elif defined(TOGL_NSOPENGL) #undef panic # include # include /* Use NSOpenGLContext */ # include /* Use NSView */ # include /* Use NSWindow */ # include /* Use NSView setWantsBestResolutionOpenGLSurface */ # include /* Use NSEvent */ # include /* Use NSTouch */ # include /* Use NSRect */ # include /* Use MacDrawable */ # include # define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr) # include # ifndef __MAC_10_7 /* Define Mac retina display routines not available prior to Mac OS 10.7 */ @interface NSView (NSOpenGLSurfaceResolution) - (BOOL)wantsBestResolutionOpenGLSurface; - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag; - (NSRect)convertRectToBacking:(NSRect)aRect; #define NSEventPhaseNone 0 @end # endif #else /* make sure only one platform defined */ # error Unsupported platform, or confused platform defines... #endif #define NC3D L"NVidia Consumer 3D Stereo" #ifndef STEREO_BUFFER_NONE /* From , but we use this constants elsewhere */ # define STEREO_BUFFER_NONE 0 # define STEREO_BUFFER_LEFT 1 # define STEREO_BUFFER_RIGHT 2 #endif /*** Standard C headers ***/ #include #include #include #ifdef TOGL_WGL # include #endif #if TK_MAJOR_VERSION < 8 # error Sorry Togl requires Tcl/Tk ver 8.0 or higher. #endif #ifdef USE_TCL_STUBS # if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 1) # error Sorry stub support requires Tcl/Tk ver 8.1 or higher. # endif #endif #if defined(TOGL_AGL) # if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 4) # error Sorry Mac Aqua version requires Tcl/Tk ver 8.4.0 or higher. # endif #endif /* TOGL_AGL */ // Seems to work with Apple Tcl 8.5 too.... // #if defined(TOGL_NSOPENGL) // # if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 6) // # error Sorry Mac Cocoa version requires Tcl/Tk ver 8.6.0 or higher. // # endif // #endif /* TOGL_NSOPENGL */ #if defined(TOGL_WGL) && defined(_MSC_VER) # define snprintf _snprintf # pragma warning(disable:4995) #endif /* workaround for bug #123153 in tcl ver8.4a2 (tcl.h) */ #if defined(Tcl_InitHashTable) && defined(USE_TCL_STUBS) # undef Tcl_InitHashTable # define Tcl_InitHashTable (tclStubsPtr->tcl_InitHashTable) #endif #if TK_MAJOR_VERSION > 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION >= 4) # define HAVE_TK_SETCLASSPROCS /* pointer to Tk_SetClassProcs function in the stub table */ #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 6 static void (*SetClassProcsPtr) _ANSI_ARGS_((Tk_Window, Tk_ClassProcs *, ClientData)); #else static void (*SetClassProcsPtr) _ANSI_ARGS_((Tk_Window, const Tk_ClassProcs *, ClientData)); #endif #endif /* * Copy of TkClassProcs declarations from tkInt.h * (this is needed for Tcl ver =< 8.4a3) */ typedef Window (TkClassCreateProc) _ANSI_ARGS_((Tk_Window tkwin, Window parent, ClientData instanceData)); typedef void (TkClassGeometryProc) _ANSI_ARGS_((ClientData instanceData)); typedef void (TkClassModalProc) _ANSI_ARGS_((Tk_Window tkwin, XEvent *eventPtr)); typedef struct TkClassProcs { TkClassCreateProc *createProc; TkClassGeometryProc *geometryProc; TkClassModalProc *modalProc; } TkClassProcs; /* Defaults */ #define DEFAULT_WIDTH "400" #define DEFAULT_HEIGHT "400" #define DEFAULT_IDENT "" #define DEFAULT_FONTNAME "Courier" #define DEFAULT_TIME "1" #ifdef TOGL_WGL /* Maximum size of a logical palette corresponding to a colormap in color index * mode. */ # define MAX_CI_COLORMAP_SIZE 4096 # define MAX_CI_COLORMAP_BITS 12 # if TOGL_USE_FONTS != 1 /* * copy of TkWinColormap from tkWinInt.h */ typedef struct { HPALETTE palette; /* Palette handle used when drawing. */ UINT size; /* Number of entries in the palette. */ int stale; /* 1 if palette needs to be realized, otherwise * 0. If the palette is stale, then an idle * handler is scheduled to realize the palette. */ Tcl_HashTable refCounts; /* Hash table of palette entry reference counts * indexed by pixel value. */ } TkWinColormap; # else # include # endif static LRESULT(CALLBACK *tkWinChildProc) (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) = NULL; # ifndef TK_WIN_CHILD_CLASS_NAME # define TK_WIN_CHILD_CLASS_NAME L"TkChild" # endif #endif /* TOGL_WGL */ #define MAX(a,b) (((a)>(b))?(a):(b)) #define TCL_ERR(interp, string) \ do { \ Tcl_ResetResult(interp); \ Tcl_AppendResult(interp, string, NULL); \ return TCL_ERROR; \ } while (0) #define ALL_EVENTS_MASK \ (KeyPressMask \ |KeyReleaseMask \ |ButtonPressMask \ |ButtonReleaseMask \ |EnterWindowMask \ |LeaveWindowMask \ |PointerMotionMask \ |ExposureMask \ |VisibilityChangeMask \ |FocusChangeMask \ |PropertyChangeMask \ |ColormapChangeMask) /* * The following structure contains pointers to functions used for * processing the custom "-stereo" option. Copied from tkPanedWindow.c. */ static int SetStereo(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj **value, char *recordPtr, int internalOffset, char *oldInternalPtr, int flags); static Tcl_Obj *GetStereo(ClientData clientData, Tk_Window tkwin, char *recordPtr, int internalOffset); static void RestoreStereo(ClientData clientData, Tk_Window tkwin, char *internalPtr, char *oldInternalPtr); static Tk_ObjCustomOption stereoOption = { "stereo", /* name */ SetStereo, /* setProc */ GetStereo, /* getProc */ RestoreStereo, /* restoreProc */ NULL, /* freeProc */ 0 }; /* * The following structure contains pointers to functions used for * processing the custom "-pixelformat" option. Copied from tkPanedWindow.c. */ static int SetWideInt(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj **value, char *recordPtr, int internalOffset, char *oldInternalPtr, int flags); static Tcl_Obj *GetWideInt(ClientData clientData, Tk_Window tkwin, char *recordPtr, int internalOffset); static void RestoreWideInt(ClientData clientData, Tk_Window tkwin, char *internalPtr, char *oldInternalPtr); static Tk_ObjCustomOption wideIntOption = { "wide int", /* name */ SetWideInt, /* setProc */ GetWideInt, /* getProc */ RestoreWideInt, /* restoreProc */ NULL, /* freeProc */ 0 }; /* * Stuff we initialize on a per package (Togl_Init) basis. * Since Tcl uses one interpreter per thread, any per-thread * data goes here. */ struct Togl_PackageGlobals { Tk_OptionTable optionTable; /* Used to parse options */ Togl *toglHead; /* Head of linked list of all Togl widgets */ int nextContextTag; /* Used to assign similar context tags */ }; typedef struct Togl_PackageGlobals Togl_PackageGlobals; extern ToglStubs toglStubs; /* should be only non-const global */ #if defined(TOGL_NSOPENGL) /* Handle window drags between retina and non-retina displays. */ @interface ToglNSView : NSView { struct Togl *togl; } - (void) setTogl: (struct Togl*)t; - (void) viewDidChangeBackingProperties; - (void) magnifyWithEvent: (NSEvent *)event; - (void) rotateWithEvent: (NSEvent *)event; - (void) scrollWheel: (NSEvent *)event; - (void)touchesBeganWithEvent:(NSEvent *)event; - (void)touchesMovedWithEvent:(NSEvent *)event; - (void)touchesEndedWithEvent:(NSEvent *)event; - (void)touchesCancelledWithEvent:(NSEvent *)event; - (void)reportEventTouches:(NSEvent *)event; @end #endif struct Togl { Togl *Next; /* next in linked list */ #if defined(TOGL_WGL) HGLRC Ctx; /* OpenGL rendering context to be made current */ HDC tglGLHdc; /* Device context of device that OpenGL calls * will be drawn on */ int CiColormapSize; /* (Maximum) size of colormap in color index * mode */ #elif defined(TOGL_X11) GLXContext Ctx; /* Normal planes GLX context */ #elif defined(TOGL_AGL) AGLContext Ctx; #elif defined(TOGL_NSOPENGL) NSOpenGLContext *Ctx; ToglNSView *nsview; #endif int contextTag; /* all contexts with same tag share display * lists */ XVisualInfo *VisInfo; /* Visual info of the current */ Display *display; /* X's token for the window's display. */ Tk_Window TkWin; /* Tk window structure */ Tcl_Interp *Interp; /* Tcl interpreter */ Tcl_Command widgetCmd; /* Token for togl's widget command */ Togl_PackageGlobals *tpg; /* Used to access globals */ #ifndef NO_TK_CURSOR Tk_Cursor Cursor; /* The widget's cursor */ #endif int Width, Height; /* Dimensions of window */ int PixelScale; /* Graphics pixels per Tk pixel. */ /* 1 for normal display. */ /* 2 for Mac retina display. */ int SetGrid; /* positive is grid size for window manager */ int TimerInterval; /* Time interval for timer in milliseconds */ Tcl_TimerToken timerHandler; /* Token for togl's timer handler */ Bool RgbaFlag; /* configuration flags (ala GLX parameters) */ int RgbaRed; int RgbaGreen; int RgbaBlue; Bool DoubleFlag; Bool DepthFlag; int DepthSize; Bool AccumFlag; int AccumRed; int AccumGreen; int AccumBlue; int AccumAlpha; Bool AlphaFlag; int AlphaSize; Bool StencilFlag; int StencilSize; Bool PrivateCmapFlag; Bool OverlayFlag; int Stereo; double EyeSeparation; double Convergence; GLuint riStencilBit; /* row interleaved stencil bit */ int AuxNumber; Bool Indirect; #if defined(TOGL_NSOPENGL) NSOpenGLPixelFormat *PixelFormat; #else Tcl_WideInt PixelFormat; #endif int SwapInterval; Bool MultisampleFlag; Bool FullscreenFlag; Bool PbufferFlag; Bool LargestPbufferFlag; #if defined(TOGL_X11) GLXFBConfig fbcfg; /* cache FBConfig for pbuffer creation */ GLXPbuffer pbuf; #elif defined(TOGL_WGL) HPBUFFERARB pbuf; int pbufferLost; #elif defined(TOGL_AGL) AGLPbuffer pbuf; #elif defined(TOGL_NSOPENGL) NSOpenGLPixelBuffer *pbuf; #endif const char *ShareList; /* name (ident) of Togl to share dlists with */ const char *ShareContext; /* name (ident) to share OpenGL context with */ const char *Ident; /* User's identification string */ ClientData Client_Data; /* Pointer to user data */ Bool UpdatePending; /* Should normal planes be redrawn? */ Tcl_Obj *CreateProc; /* Callback when widget is realized */ Tcl_Obj *DisplayProc; /* Callback when widget is redrawn */ Tcl_Obj *ReshapeProc; /* Callback when window size changes */ Tcl_Obj *DestroyProc; /* Callback when widget is destroyed */ Tcl_Obj *TimerProc; /* Callback when widget is idle */ Tcl_Obj *MagnifyProc; /* Callback for track pad pinch gesture */ Tcl_Obj *RotateProc; /* Callback for track pad rotate gesture */ Tcl_Obj *ScrollProc; /* Callback for track pad scroll gesture */ Tcl_Obj *ScrollWheelProc; /* Callback for mouse scroll wheel, not trackpad */ Tcl_Obj *TouchesProc; /* Callback for track pad touches */ /* Overlay stuff */ #if defined(TOGL_X11) GLXContext OverlayCtx; /* Overlay planes OpenGL context */ #elif defined(TOGL_WGL) HGLRC tglGLOverlayHglrc; #endif Window OverlayWindow; /* The overlay window, or 0 */ Tcl_Obj *OverlayDisplayProc; /* Overlay redraw proc */ Bool OverlayUpdatePending; /* Should overlay be redrawn? */ Colormap OverlayCmap; /* colormap for overlay is created */ int OverlayTransparentPixel; /* transparent pixel */ Bool OverlayIsMapped; GLfloat *RedMap; /* Index2RGB Maps for Color index modes */ GLfloat *GreenMap; GLfloat *BlueMap; GLint MapSize; /* = Number of indices in our Togl */ int currentStereoBuffer; #ifdef HAVE_AUTOSTEREO int as_initialized; /* for autostereo package */ ASHandle ash; /* for autostereo package */ #endif int badWindow; /* true when Togl_MakeWindow fails or should * create a dummy window */ }; int Togl_CallCallback(Togl *togl, Tcl_Obj *cmd); static int Togl_CallCallback_P(Togl *togl, Tcl_Obj *cmd, double *params, int nparams); #if defined(TOGL_NSOPENGL) static int viewPixelScale(NSView *nsview); @implementation ToglNSView - (void) setTogl: (struct Togl*)t { togl = t; } - (void) viewDidChangeBackingProperties { [super viewDidChangeBackingProperties]; if (togl && togl->ReshapeProc) { int pscale = viewPixelScale(self); if (pscale != togl->PixelScale) { togl->PixelScale = pscale; if (togl->ReshapeProc) (void) Togl_CallCallback(togl, togl->ReshapeProc); Togl_PostRedisplay(togl); } } } - (void) magnifyWithEvent: (NSEvent *)event { if (togl && togl->MagnifyProc) { double mag = [event magnification] + 1.0; (void) Togl_CallCallback_P(togl, togl->MagnifyProc, &mag, 1); } } - (void) rotateWithEvent: (NSEvent *)event { if (togl && togl->RotateProc) { double deg = [event rotation]; (void) Togl_CallCallback_P(togl, togl->RotateProc, °, 1); } } - (void) scrollWheel: (NSEvent *)event { if (togl && (togl->ScrollProc || togl->ScrollWheelProc)) { float dx = [event deltaX], dy = [event deltaY]; if (dx != 0 || dy != 0) { // float track_pad = ((([event phase] == NSEventPhaseNone) && // ([event momentumPhase] == NSEventPhaseNone)) ? 0 : 1); float track_pad = ([event subtype] == NSMouseEventSubtype ? 0 : 1); double args[3] = {dx, dy, track_pad}; if (togl->ScrollProc) (void) Togl_CallCallback_P(togl, togl->ScrollProc, args, 3); if (togl->ScrollWheelProc && !track_pad) (void) Togl_CallCallback_P(togl, togl->ScrollWheelProc, args+1, 1); } } } - (void)touchesBeganWithEvent:(NSEvent *)event { NSWindow *win = [self window]; [win makeKeyWindow]; [win makeMainWindow]; [self reportEventTouches:event]; } - (void)touchesMovedWithEvent:(NSEvent *)event { [self reportEventTouches:event]; } - (void)touchesEndedWithEvent:(NSEvent *)event { [self reportEventTouches:event]; } - (void)touchesCancelledWithEvent:(NSEvent *)event; { [self reportEventTouches:event]; } - (void)reportEventTouches: (NSEvent *)event { if (togl && togl->TouchesProc) { NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseTouching inView:self]; int n = touches.count, i; double *id_xy = NULL; if (n >= 0) { id_xy = (double *)malloc(3*n*sizeof(double)); NSArray *array = [touches allObjects]; for (i = 0 ; i < n ; ++i) { NSTouch *t = [array objectAtIndex:i]; id_xy[3*i] = (long)t.identity; id_xy[3*i+1] = t.normalizedPosition.x * t.deviceSize.width; id_xy[3*i+2] = t.normalizedPosition.y * t.deviceSize.height; } (void) Togl_CallCallback_P(togl, togl->TouchesProc, id_xy, 3*n); if (id_xy) free(id_xy); } } } @end #endif /* * Prototypes for functions local to this file */ static int Togl_ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); static void Togl_ObjCmdDelete(ClientData clientData); static void Togl_EventProc(ClientData clientData, XEvent *eventPtr); static void Togl_RedisplayProc(ClientData clientData, XEvent *eventPtr); static Window Togl_MakeWindow(Tk_Window, Window, ClientData); static void Togl_WorldChanged(ClientData); static void Togl_SetViewPort(const struct Togl *); #ifdef MESA_COLOR_HACK static int get_free_color_cells(Display *display, int screen, Colormap colormap); static void free_default_color_cells(Display *display, Colormap colormap); #endif static void ToglCmdDeletedProc(ClientData); #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) static void SetMacBufRect(Togl *togl); #endif #if defined(TOGL_WGL) # include "toglWGL.c" #elif defined(TOGL_AGL) # include "toglAGL.c" #elif defined(TOGL_NSOPENGL) # include "toglNSOpenGL.c" #elif defined(TOGL_X11) # include "toglGLX.c" #endif /* * Setup Togl widget configuration options: */ #define GEOMETRY_MASK 0x1 /* widget geometry */ #define FORMAT_MASK 0x2 /* pixel format */ #define CURSOR_MASK 0x4 #define TIMER_MASK 0x8 #define OVERLAY_MASK 0x10 #define SWAP_MASK 0x20 #define STEREO_MASK 0x40 #define STEREO_FORMAT_MASK 0x80 static Tk_OptionSpec optionSpecs[] = { {TK_OPTION_PIXELS, TCL_STUPID "-height", "height", "Height", DEFAULT_HEIGHT, -1, Tk_Offset(Togl, Height), 0, NULL, GEOMETRY_MASK}, {TK_OPTION_PIXELS, TCL_STUPID "-width", "width", "Width", DEFAULT_WIDTH, -1, Tk_Offset(Togl, Width), 0, NULL, GEOMETRY_MASK}, {TK_OPTION_INT, TCL_STUPID "-pixelscale", "pixelscale", "PixelScale", "1", -1, Tk_Offset(Togl, PixelScale), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-rgba", "rgba", "Rgba", "true", -1, Tk_Offset(Togl, RgbaFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-redsize", "redsize", "RedSize", "1", -1, Tk_Offset(Togl, RgbaRed), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-greensize", "greensize", "GreenSize", "1", -1, Tk_Offset(Togl, RgbaGreen), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-bluesize", "bluesize", "BlueSize", "1", -1, Tk_Offset(Togl, RgbaBlue), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-double", "double", "Double", "false", -1, Tk_Offset(Togl, DoubleFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-depth", "depth", "Depth", "false", -1, Tk_Offset(Togl, DepthFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-depthsize", "depthsize", "DepthSize", "1", -1, Tk_Offset(Togl, DepthSize), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-accum", "accum", "Accum", "false", -1, Tk_Offset(Togl, AccumFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-accumredsize", "accumredsize", "AccumRedSize", "1", -1, Tk_Offset(Togl, AccumRed), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-accumgreensize", "accumgreensize", "AccumGreenSize", "1", -1, Tk_Offset(Togl, AccumGreen), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-accumbluesize", "accumbluesize", "AccumBlueSize", "1", -1, Tk_Offset(Togl, AccumBlue), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-accumalphasize", "accumalphasize", "AccumAlphaSize", "1", -1, Tk_Offset(Togl, AccumAlpha), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-alpha", "alpha", "Alpha", "false", -1, Tk_Offset(Togl, AlphaFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-alphasize", "alphasize", "AlphaSize", "1", -1, Tk_Offset(Togl, AlphaSize), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-stencil", "stencil", "Stencil", "false", -1, Tk_Offset(Togl, StencilFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-stencilsize", "stencilsize", "StencilSize", "1", -1, Tk_Offset(Togl, StencilSize), 0, NULL, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-auxbuffers", "auxbuffers", "AuxBuffers", "0", -1, Tk_Offset(Togl, AuxNumber), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-privatecmap", "privateCmap", "PrivateCmap", "false", -1, Tk_Offset(Togl, PrivateCmapFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-overlay", "overlay", "Overlay", "false", -1, Tk_Offset(Togl, OverlayFlag), 0, NULL, OVERLAY_MASK}, {TK_OPTION_CUSTOM, TCL_STUPID "-stereo", "stereo", "Stereo", "", -1, Tk_Offset(Togl, Stereo), 0, (ClientData) &stereoOption, STEREO_FORMAT_MASK}, {TK_OPTION_DOUBLE, TCL_STUPID "-eyeseparation", "eyeseparation", "EyeSeparation", "2.0", -1, Tk_Offset(Togl, EyeSeparation), 0, NULL, STEREO_MASK}, {TK_OPTION_DOUBLE, TCL_STUPID "-convergence", "convergence", "Convergence", "35.0", -1, Tk_Offset(Togl, Convergence), 0, NULL, STEREO_MASK}, #ifndef NO_TK_CURSOR {TK_OPTION_CURSOR, TCL_STUPID "-cursor", "cursor", "Cursor", "", -1, Tk_Offset(Togl, Cursor), TK_OPTION_NULL_OK, NULL, CURSOR_MASK}, #endif {TK_OPTION_INT, TCL_STUPID "-setgrid", "setGrid", "SetGrid", "0", -1, Tk_Offset(Togl, SetGrid), 0, NULL, GEOMETRY_MASK}, {TK_OPTION_INT, TCL_STUPID "-time", "time", "Time", DEFAULT_TIME, -1, Tk_Offset(Togl, TimerInterval), 0, NULL, TIMER_MASK}, {TK_OPTION_STRING, TCL_STUPID "-sharelist", "sharelist", "ShareList", NULL, -1, Tk_Offset(Togl, ShareList), 0, NULL, FORMAT_MASK}, {TK_OPTION_STRING, TCL_STUPID "-sharecontext", "sharecontext", "ShareContext", NULL, -1, Tk_Offset(Togl, ShareContext), 0, NULL, FORMAT_MASK}, {TK_OPTION_STRING, TCL_STUPID "-ident", "ident", "Ident", DEFAULT_IDENT, -1, Tk_Offset(Togl, Ident), 0, NULL, 0}, {TK_OPTION_BOOLEAN, TCL_STUPID "-indirect", "indirect", "Indirect", "false", -1, Tk_Offset(Togl, Indirect), 0, NULL, FORMAT_MASK}, {TK_OPTION_CUSTOM, TCL_STUPID "-pixelformat", "pixelFormat", "PixelFormat", "0", -1, Tk_Offset(Togl, PixelFormat), 0, (ClientData) &wideIntOption, FORMAT_MASK}, {TK_OPTION_INT, TCL_STUPID "-swapinterval", "swapInterval", "SwapInterval", "1", -1, Tk_Offset(Togl, SwapInterval), 0, NULL, SWAP_MASK}, #if 0 {TK_OPTION_BOOLEAN, TCL_STUPID "-fullscreen", "fullscreen", "Fullscreen", "false", -1, Tk_Offset(Togl, FullscreenFlag), 0, NULL, GEOMETRY_MASK|FORMAT_MASK}, #endif {TK_OPTION_BOOLEAN, TCL_STUPID "-multisample", "multisample", "Multisample", "false", -1, Tk_Offset(Togl, MultisampleFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-pbuffer", "pbuffer", "Pbuffer", "false", -1, Tk_Offset(Togl, PbufferFlag), 0, NULL, FORMAT_MASK}, {TK_OPTION_BOOLEAN, TCL_STUPID "-largestpbuffer", "largestpbuffer", "LargestPbuffer", "false", -1, Tk_Offset(Togl, LargestPbufferFlag), 0, NULL, 0}, {TK_OPTION_STRING, TCL_STUPID "-createcommand", "createCommand", "CallbackCommand", NULL, Tk_Offset(Togl, CreateProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_SYNONYM, TCL_STUPID "-create", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-createcommand", 0}, {TK_OPTION_STRING, TCL_STUPID "-displaycommand", "displayCommand", "CallbackCommand", NULL, Tk_Offset(Togl, DisplayProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_SYNONYM, TCL_STUPID "-display", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-displaycommand", 0}, {TK_OPTION_STRING, TCL_STUPID "-reshapecommand", "reshapeCommand", "CallbackCommand", NULL, Tk_Offset(Togl, ReshapeProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_SYNONYM, TCL_STUPID "-reshape", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-reshapecommand", 0}, {TK_OPTION_STRING, TCL_STUPID "-destroycommand", "destroyCommand", "CallbackCommand", NULL, Tk_Offset(Togl, DestroyProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_SYNONYM, TCL_STUPID "-destroy", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-destroycommand", 0}, {TK_OPTION_STRING, TCL_STUPID "-timercommand", "timerCommand", "CallabckCommand", NULL, Tk_Offset(Togl, TimerProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_SYNONYM, TCL_STUPID "-timer", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-timercommand", 0}, {TK_OPTION_STRING, TCL_STUPID "-overlaydisplaycommand", "overlaydisplayCommand", "CallbackCommand", NULL, Tk_Offset(Togl, OverlayDisplayProc), -1, TK_OPTION_NULL_OK, NULL, OVERLAY_MASK}, {TK_OPTION_SYNONYM, TCL_STUPID "-overlaydisplay", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-overlaydisplaycommand", 0}, {TK_OPTION_STRING, TCL_STUPID "-magnifycommand", "magnifyCommand", "CallbackCommand", NULL, Tk_Offset(Togl, MagnifyProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_STRING, TCL_STUPID "-rotatecommand", "rotateCommand", "CallbackCommand", NULL, Tk_Offset(Togl, RotateProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_STRING, TCL_STUPID "-scrollcommand", "scrollCommand", "CallbackCommand", NULL, Tk_Offset(Togl, ScrollProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_STRING, TCL_STUPID "-scrollwheelcommand", "scrollWheelCommand", "CallbackCommand", NULL, Tk_Offset(Togl, ScrollWheelProc), -1, TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_STRING, TCL_STUPID "-touchescommand", "touchesCommand", "CallbackCommand", NULL, Tk_Offset(Togl, TouchesProc), -1, TK_OPTION_NULL_OK, NULL, 0}, /* Tcl3D backwards compatibility */ {TK_OPTION_SYNONYM, TCL_STUPID "-createproc", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-createcommand", 0}, {TK_OPTION_SYNONYM, TCL_STUPID "-displayproc", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-displaycommand", 0}, {TK_OPTION_SYNONYM, TCL_STUPID "-reshapeproc", NULL, NULL, NULL, -1, -1, 0, (ClientData) "-reshapecommand", 0}, /* end Tcl3D compatibility */ {TK_OPTION_END, NULL, NULL, NULL, NULL, -1, -1, 0, NULL, 0} }; /* * Add given togl widget to linked list. */ static void AddToList(Togl *t) { t->Next = t->tpg->toglHead; t->tpg->toglHead = t; } /* * Remove given togl widget from linked list. */ static void RemoveFromList(Togl *t) { Togl *prev; Togl *cur; for (cur = t->tpg->toglHead, prev = NULL; cur; prev = cur, cur = cur->Next) { if (t != cur) continue; if (prev) { prev->Next = cur->Next; } else { t->tpg->toglHead = cur->Next; } break; } if (cur) cur->Next = NULL; } /* * Return pointer to togl widget given a user identifier string. */ static Togl * FindTogl(Togl *togl, const char *ident) { Togl *t; if (ident[0] != '.') { for (t = togl->tpg->toglHead; t; t = t->Next) { if (strcmp(t->Ident, ident) == 0) break; } } else { for (t = togl->tpg->toglHead; t; t = t->Next) { const char *pathname = Tk_PathName(t->TkWin); if (strcmp(pathname, ident) == 0) break; } } return t; } /* * Return pointer to another togl widget with same OpenGL context. */ static Togl * FindToglWithSameContext(const Togl *togl) { Togl *t; for (t = togl->tpg->toglHead; t != NULL; t = t->Next) { if (t == togl) continue; if (t->Ctx == togl->Ctx) { return t; } } return NULL; } #if TOGL_USE_OVERLAY /* * Return pointer to another togl widget with same OpenGL overlay context. */ static Togl * FindToglWithSameOverlayContext(const Togl *togl) { Togl *t; for (t = togl->tpg->toglHead; t != NULL; t = t->Next) { if (t == togl) continue; # if defined(TOGL_X11) if (t->OverlayCtx == togl->OverlayCtx) # elif defined(TOGL_WGL) if (t->tglGLOverlayHglrc == togl->tglGLOverlayHglrc) # endif { return t; } } return NULL; } #endif #if defined(TOGL_X11) /* * Return an X colormap to use for OpenGL RGB-mode rendering. * Input: dpy - the X display * scrnum - the X screen number * visinfo - the XVisualInfo as returned by glXChooseVisual() * Return: an X Colormap or 0 if there's a _serious_ error. */ static Colormap get_rgb_colormap(Display *dpy, int scrnum, const XVisualInfo *visinfo, Tk_Window tkwin) { Atom hp_cr_maps; Status status; int numCmaps; int i; XStandardColormap *standardCmaps; Window root = XRootWindow(dpy, scrnum); Bool using_mesa; /* * First check if visinfo's visual matches the default/root visual. */ if (visinfo->visual == Tk_Visual(tkwin)) { /* use the default/root colormap */ Colormap cmap; cmap = Tk_Colormap(tkwin); # ifdef MESA_COLOR_HACK (void) get_free_color_cells(dpy, scrnum, cmap); # endif return cmap; } /* * Check if we're using Mesa. */ if (strstr(glXQueryServerString(dpy, scrnum, GLX_VERSION), "Mesa")) { using_mesa = True; } else { using_mesa = False; } /* * Next, if we're using Mesa and displaying on an HP with the "Color * Recovery" feature and the visual is 8-bit TrueColor, search for a * special colormap initialized for dithering. Mesa will know how to * dither using this colormap. */ if (using_mesa) { hp_cr_maps = XInternAtom(dpy, "_HP_RGB_SMOOTH_MAP_LIST", True); if (hp_cr_maps # ifdef __cplusplus && visinfo->visual->c_class == TrueColor # else && visinfo->visual->class == TrueColor # endif && visinfo->depth == 8) { status = XGetRGBColormaps(dpy, root, &standardCmaps, &numCmaps, hp_cr_maps); if (status) { for (i = 0; i < numCmaps; i++) { if (standardCmaps[i].visualid == visinfo->visual->visualid) { Colormap cmap = standardCmaps[i].colormap; (void) XFree(standardCmaps); return cmap; } } (void) XFree(standardCmaps); } } } /* * Next, try to find a standard X colormap. */ # if !HP && !SUN # ifndef SOLARIS_BUG status = XmuLookupStandardColormap(dpy, visinfo->screen, visinfo->visualid, visinfo->depth, XA_RGB_DEFAULT_MAP, /* replace */ False, /* retain */ True); if (status == 1) { status = XGetRGBColormaps(dpy, root, &standardCmaps, &numCmaps, XA_RGB_DEFAULT_MAP); if (status == 1) { for (i = 0; i < numCmaps; i++) { if (standardCmaps[i].visualid == visinfo->visualid) { Colormap cmap = standardCmaps[i].colormap; (void) XFree(standardCmaps); return cmap; } } (void) XFree(standardCmaps); } } # endif # endif /* * If we get here, give up and just allocate a new colormap. */ return XCreateColormap(dpy, root, visinfo->visual, AllocNone); } #elif defined(TOGL_WGL) /* Code to create RGB palette is taken from the GENGL sample program of Win32 * SDK */ static const unsigned char threeto8[8] = { 0, 0111 >> 1, 0222 >> 1, 0333 >> 1, 0444 >> 1, 0555 >> 1, 0666 >> 1, 0377 }; static const unsigned char twoto8[4] = { 0, 0x55, 0xaa, 0xff }; static const unsigned char oneto8[2] = { 0, 255 }; static const int defaultOverride[13] = { 0, 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91 }; static const PALETTEENTRY defaultPalEntry[20] = { {0, 0, 0, 0}, {0x80, 0, 0, 0}, {0, 0x80, 0, 0}, {0x80, 0x80, 0, 0}, {0, 0, 0x80, 0}, {0x80, 0, 0x80, 0}, {0, 0x80, 0x80, 0}, {0xC0, 0xC0, 0xC0, 0}, {192, 220, 192, 0}, {166, 202, 240, 0}, {255, 251, 240, 0}, {160, 160, 164, 0}, {0x80, 0x80, 0x80, 0}, {0xFF, 0, 0, 0}, {0, 0xFF, 0, 0}, {0xFF, 0xFF, 0, 0}, {0, 0, 0xFF, 0}, {0xFF, 0, 0xFF, 0}, {0, 0xFF, 0xFF, 0}, {0xFF, 0xFF, 0xFF, 0} }; static unsigned char ComponentFromIndex(int i, UINT nbits, UINT shift) { unsigned char val; val = (unsigned char) (i >> shift); switch (nbits) { case 1: val &= 0x1; return oneto8[val]; case 2: val &= 0x3; return twoto8[val]; case 3: val &= 0x7; return threeto8[val]; default: return 0; } } static Colormap Win32CreateRgbColormap(PIXELFORMATDESCRIPTOR pfd) { TkWinColormap *cmap = (TkWinColormap *) ckalloc(sizeof (TkWinColormap)); LOGPALETTE *pPal; int n, i; n = 1 << pfd.cColorBits; pPal = (PLOGPALETTE) LocalAlloc(LMEM_FIXED, sizeof (LOGPALETTE) + n * sizeof (PALETTEENTRY)); pPal->palVersion = 0x300; pPal->palNumEntries = n; for (i = 0; i < n; i++) { pPal->palPalEntry[i].peRed = ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift); pPal->palPalEntry[i].peGreen = ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift); pPal->palPalEntry[i].peBlue = ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift); pPal->palPalEntry[i].peFlags = 0; } /* fix up the palette to include the default GDI palette */ if ((pfd.cColorBits == 8) && (pfd.cRedBits == 3) && (pfd.cRedShift == 0) && (pfd.cGreenBits == 3) && (pfd.cGreenShift == 3) && (pfd.cBlueBits == 2) && (pfd.cBlueShift == 6)) { for (i = 1; i <= 12; i++) pPal->palPalEntry[defaultOverride[i]] = defaultPalEntry[i]; } cmap->palette = CreatePalette(pPal); LocalFree(pPal); cmap->size = n; cmap->stale = 0; /* Since this is a private colormap of a fix size, we do not need a valid * hash table, but a dummy one */ Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS); return (Colormap) cmap; } static Colormap Win32CreateCiColormap(Togl *togl) { /* Create a colormap with size of togl->CiColormapSize and set all entries * to black */ LOGPALETTE logPalette; TkWinColormap *cmap = (TkWinColormap *) ckalloc(sizeof (TkWinColormap)); logPalette.palVersion = 0x300; logPalette.palNumEntries = 1; logPalette.palPalEntry[0].peRed = 0; logPalette.palPalEntry[0].peGreen = 0; logPalette.palPalEntry[0].peBlue = 0; logPalette.palPalEntry[0].peFlags = 0; cmap->palette = CreatePalette(&logPalette); cmap->size = togl->CiColormapSize; ResizePalette(cmap->palette, cmap->size); /* sets new entries to black */ cmap->stale = 0; /* Since this is a private colormap of a fix size, we do not need a valid * hash table, but a dummy one */ Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS); return (Colormap) cmap; } /* ErrorExit is from */ static void ErrorExit(LPTSTR lpszFunction) { /* Retrieve the system error message for the last-error code */ LPTSTR lpMsgBuf; LPTSTR lpDisplayBuf; DWORD err = GetLastError(); if (err == 0) { /* The function said it failed, but GetLastError says it didn't, so * pretend it didn't. */ return; } FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); /* Display the error message and exit the process */ lpDisplayBuf = (LPTSTR) LocalAlloc(LMEM_ZEROINIT, (lstrlen(lpMsgBuf) + lstrlen(lpszFunction) + 40) * sizeof (TCHAR)); StringCchPrintf(lpDisplayBuf, LocalSize(lpDisplayBuf), TEXT("%s failed with error %ld: %s"), lpszFunction, err, lpMsgBuf); MessageBox(NULL, lpDisplayBuf, TEXT("Error"), MB_OK); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); ExitProcess(err); } #endif /* * Togl_Init * * Called upon system startup to create togl command. */ int Togl_Init(Tcl_Interp *interp) { int major, minor, patchLevel, releaseType; #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { return TCL_ERROR; } #endif #ifdef USE_TK_STUBS if (Tk_InitStubs(interp, TCL_STUPID "8.1", 0) == NULL) { return TCL_ERROR; } #endif Tcl_GetVersion(&major, &minor, &patchLevel, &releaseType); #ifdef HAVE_TK_SETCLASSPROCS if (major > 8 || (major == 8 && (minor > 4 || (minor == 4 && (releaseType > 0 || patchLevel >= 2))))) { # ifdef USE_TK_STUBS SetClassProcsPtr = tkStubsPtr->tk_SetClassProcs; # else SetClassProcsPtr = Tk_SetClassProcs; # endif } else { SetClassProcsPtr = NULL; } #else if (major > 8 || (major == 8 && (minor > 4 || (minor == 4 && (releaseType > 0 || patchLevel >= 2))))) { TCL_ERR(interp, "Sorry, this instance of Togl was not compiled to work with Tcl/Tk 8.4a2 or higher."); } #endif if (Tcl_CreateObjCommand(interp, "togl", Togl_ObjCmd, NULL, Togl_ObjCmdDelete) == NULL) { return TCL_ERROR; } if (Tcl_PkgProvideEx(interp, "Togl", TOGL_VERSION, &toglStubs) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } /* * Togl_CallCallback * * Call command with togl widget as only argument */ int Togl_CallCallback(Togl *togl, Tcl_Obj *cmd) { int result; Tcl_Obj *objv[3]; if (cmd == NULL || togl->widgetCmd == NULL) return TCL_OK; objv[0] = cmd; Tcl_IncrRefCount(objv[0]); objv[1] = Tcl_NewStringObj(Tcl_GetCommandName(togl->Interp, togl->widgetCmd), -1); Tcl_IncrRefCount(objv[1]); objv[2] = NULL; result = Tcl_EvalObjv(togl->Interp, 2, objv, TCL_EVAL_GLOBAL); Tcl_DecrRefCount(objv[1]); Tcl_DecrRefCount(objv[0]); if (result != TCL_OK) Tcl_BackgroundError(togl->Interp); return result; } static int Togl_CallCallback_P(Togl *togl, Tcl_Obj *cmd, double *params, int nparams) { int result, i; Tcl_Obj **objv = (Tcl_Obj **)malloc((3+nparams)*sizeof(Tcl_Obj *)); if (cmd == NULL || togl->widgetCmd == NULL) return TCL_OK; objv[0] = cmd; Tcl_IncrRefCount(objv[0]); objv[1] = Tcl_NewStringObj(Tcl_GetCommandName(togl->Interp, togl->widgetCmd), -1); Tcl_IncrRefCount(objv[1]); for (i = 0 ; i < nparams ; ++i) { objv[2+i] = Tcl_NewDoubleObj(params[i]); Tcl_IncrRefCount(objv[2+i]); } objv[2+nparams] = NULL; result = Tcl_EvalObjv(togl->Interp, 2+nparams, objv, TCL_EVAL_GLOBAL); for (i = 1+nparams ; i >= 0 ; --i) Tcl_DecrRefCount(objv[i]); free(objv); if (result != TCL_OK) Tcl_BackgroundError(togl->Interp); return result; } /* * Togl_Timer * * Gets called from Tk_CreateTimerHandler. */ static void Togl_Timer(ClientData clientData) { Togl *togl = (Togl *) clientData; if (togl->TimerProc) { if (Togl_CallCallback(togl, togl->TimerProc) != TCL_OK) { togl->timerHandler = NULL; return; } /* * Re-register this callback since Tcl/Tk timers are "one-shot". * That is, after the timer callback is called it not normally * called again. That's not the behavior we want for Togl. */ togl->timerHandler = Tcl_CreateTimerHandler(togl->TimerInterval, Togl_Timer, (ClientData) togl); } } /* * Togl_MakeCurrent * * Bind the OpenGL rendering context to the specified * Togl widget. If given a NULL argument, then the * OpenGL context is released without assigning a new one. */ void Togl_MakeCurrent(const Togl *togl) { #if defined(TOGL_WGL) int res = TRUE; if (togl == NULL) { HDC hdc = wglGetCurrentDC(); if (hdc != NULL) res = wglMakeCurrent(hdc, NULL); } else { if (togl->pbufferLost) { Bool keepContext = FindToglWithSameContext(togl) != NULL; Togl *t = (Togl *) togl; /* conceptually const */ if (!keepContext) { wglDeleteContext(t->Ctx); } togl_destroyPbuffer(t); t->pbuf = togl_createPbuffer(t); if (!keepContext) { t->Ctx = wglCreateContext(t->tglGLHdc); } } res = wglMakeCurrent(togl->tglGLHdc, togl->Ctx); } if (!res) { ErrorExit(TEXT("wglMakeCurrent")); } #elif defined(TOGL_X11) Display *display = togl ? togl->display : glXGetCurrentDisplay(); if (display) { GLXDrawable drawable; if (!togl) drawable = None; else if (togl->PbufferFlag) drawable = togl->pbuf; else if (togl->TkWin) drawable = Tk_WindowId(togl->TkWin); else drawable = None; (void) glXMakeCurrent(display, drawable, drawable ? togl->Ctx : NULL); } #elif defined(TOGL_AGL) if (togl == NULL || togl->Ctx == NULL) { (void) aglSetCurrentContext(NULL); } else { (void) aglSetCurrentContext(togl->Ctx); if (FindToglWithSameContext(togl) != NULL) { if (!togl->PbufferFlag) { AGLDrawable d = Togl_MacOSXGetDrawablePort(togl); aglSetDrawable(togl->Ctx, d); } else { GLint virtualScreen = aglGetVirtualScreen(togl->Ctx); aglSetPBuffer(togl->Ctx, togl->pbuf, 0, 0, virtualScreen); } } } #elif defined(TOGL_NSOPENGL) if (togl != NULL && togl->Ctx != NULL) { [togl->Ctx makeCurrentContext]; if (FindToglWithSameContext(togl) != NULL) { if (!togl->PbufferFlag) { [togl->Ctx setView:togl->nsview]; } else { GLint virtualScreen = [togl->Ctx currentVirtualScreen]; [togl->Ctx setPixelBuffer:togl->pbuf cubeMapFace:0 mipMapLevel:0 currentVirtualScreen:virtualScreen]; } } } #endif } /* * Togl_TakePhoto * * Take a photo image of the current OpenGL window. May have problems * if window is partially obscured, either by other windows or by the * edges of the display. */ int Togl_TakePhoto(Togl *togl, Tk_PhotoHandle photo) { GLubyte *buffer; Tk_PhotoImageBlock photoBlock; int y, midy; unsigned char *cp; int width = togl->Width, height = togl->Height; /* * TIP #116 altered Tk_PhotoPutBlock API to add interp arg that 8.4 * doesn't have. * We need to remove that for compiling with 8.4. */ #if (TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION < 5) # define TK_PHOTOPUTBLOCK(interp, hdl, blk, x, y, w, h, cr) \ Tk_PhotoPutBlock(hdl, blk, x, y, w, h, cr) #else # define TK_PHOTOPUTBLOCK Tk_PhotoPutBlock #endif buffer = (GLubyte *) ckalloc(width * height * 4); photoBlock.pixelPtr = buffer; photoBlock.width = width; photoBlock.height = height; photoBlock.pitch = width * 4; photoBlock.pixelSize = 4; photoBlock.offset[0] = 0; photoBlock.offset[1] = 1; photoBlock.offset[2] = 2; photoBlock.offset[3] = 3; if (!togl->RgbaFlag) { #if defined(TOGL_WGL) /* Due to the lack of a unique inverse mapping from the frame buffer to * the logical palette we need a translation map from the complete * logical palette. */ int n, i; TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin); LPPALETTEENTRY entry = (LPPALETTEENTRY) malloc(togl->MapSize * sizeof (PALETTEENTRY)); n = GetPaletteEntries(cmap->palette, 0, togl->MapSize, entry); for (i = 0; i < n; i++) { togl->RedMap[i] = (GLfloat) (entry[i].peRed / 255.0); togl->GreenMap[i] = (GLfloat) (entry[i].peGreen / 255.0); togl->BlueMap[i] = (GLfloat) (entry[i].peBlue / 255.0); } free(entry); #endif /* TOGL_WGL */ glPixelMapfv(GL_PIXEL_MAP_I_TO_R, togl->MapSize, togl->RedMap); glPixelMapfv(GL_PIXEL_MAP_I_TO_G, togl->MapSize, togl->GreenMap); glPixelMapfv(GL_PIXEL_MAP_I_TO_B, togl->MapSize, togl->BlueMap); } glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); glPixelStorei(GL_PACK_ALIGNMENT, 4); /* guarantee performance */ glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); #if 1 glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); /* Some OpenGL drivers are buggy and return zero for Alpha instead of one * for RGB pixel formats. If that is happening to you, upgrade your * graphics driver. */ /* OpenGL's origin is bottom-left, Tk Photo image's is top-left, so mirror * the rows around the middle row. */ midy = height / 2; cp = buffer; for (y = 0; y < midy; ++y) { int m_y = height - 1 - y; /* mirror y */ unsigned char *m_cp = buffer + m_y * photoBlock.pitch; int x; for (x = 0; x < photoBlock.pitch; ++x) { unsigned char c = *cp; *cp++ = *m_cp; *m_cp++ = c; } } #else /* OpenGL's origin is bottom-left, Tk Photo image's is top-left, so save * rows in reverse order. */ glPixelStorei(GL_PACK_ROW_LENGTH, width); glPixelStorei(GL_PACK_SKIP_ROWS, -1); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer + width * (height - 1) * 4); #endif TK_PHOTOPUTBLOCK(togl->Interp, photo, &photoBlock, 0, 0, width, height, TK_PHOTO_COMPOSITE_SET); glPopClientAttrib(); /* restore PACK_ALIGNMENT */ ckfree((char *) buffer); return TCL_OK; } Bool Togl_SwapInterval(const Togl *togl, int interval) { #ifdef TOGL_AGL GLint swapInterval = interval; return aglSetInteger(togl->Ctx, AGL_SWAP_INTERVAL, &swapInterval); #endif #ifdef TOGL_NSOPENGL GLint swapInterval = interval; [togl->Ctx setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; return True; #endif #ifdef TOGL_WGL typedef BOOL (WINAPI *BOOLFuncInt) (int); typedef const char *(WINAPI *StrFuncHDC) (HDC); static BOOLFuncInt swapInterval = NULL; static BOOL initialized = False; if (!initialized) { const char *extensions; StrFuncHDC getExtensionsString; getExtensionsString = (StrFuncHDC) wglGetProcAddress("wglGetExtensionsStringARB"); if (getExtensionsString == NULL) getExtensionsString = (StrFuncHDC) wglGetProcAddress("wglGetExtensionsStringEXT"); if (getExtensionsString) { extensions = getExtensionsString(togl->tglGLHdc); if (strstr(extensions, "WGL_EXT_swap_control") != NULL) { swapInterval = (BOOLFuncInt) wglGetProcAddress("wglSwapIntervalEXT"); } } initialized = True; } if (swapInterval) return swapInterval(interval); return False; #endif #ifdef TOGL_X11 typedef int (*IntFuncInt) (int); static IntFuncInt swapInterval = NULL; static int initialized = False; if (!initialized) { const char *extensions = glXQueryExtensionsString(togl->display, Tk_ScreenNumber(togl->TkWin)); if (strstr(extensions, "GLX_SGI_swap_control") != NULL) { swapInterval = (IntFuncInt) Togl_GetProcAddr("glXSwapIntervalSGI"); } else if (strstr(extensions, "GLX_MESA_swap_control") != NULL) { swapInterval = (IntFuncInt) Togl_GetProcAddr("glXSwapIntervalMESA"); } initialized = True; } if (swapInterval) return swapInterval(interval) == 0; return False; #endif } #if defined(TOGL_AGL) /* tell OpenGL which part of the Mac window to render to */ static void SetMacBufRect(Togl *togl) { GLint wrect[4]; Rect r; MacDrawable *d = ((TkWindow *) togl->TkWin)->privatePtr; /* set wrect[0,1] to lower left corner of widget */ wrect[2] = Tk_Width(togl->TkWin); wrect[3] = Tk_Height(togl->TkWin); wrect[0] = d->xOff; GetPortBounds(Togl_MacOSXGetDrawablePort(togl), &r); wrect[1] = r.bottom - wrect[3] - d->yOff; if (togl->FullscreenFlag) { aglEnable(togl->Ctx, AGL_FS_CAPTURE_SINGLE); aglSetFullScreen(togl->Ctx, 0, 0, 0, 0); } else { aglUpdateContext(togl->Ctx); } aglSetInteger(togl->Ctx, AGL_BUFFER_RECT, wrect); aglEnable(togl->Ctx, AGL_BUFFER_RECT); } static void ReconfigureCB(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *closure) { /* Display reconfiguration callback. Documented as needed by Apple QA1209. * Updated for 10.3 (and later) to use * CGDisplayRegisterReconfigurationCallback. */ Togl *togl = (Togl *) closure; if (0 != (flags & kCGDisplayBeginConfigurationFlag)) return; /* wait until display is reconfigured */ SetMacBufRect(togl); Togl_MakeCurrent(togl); if (togl->Ctx) { if (togl->ReshapeProc) { Togl_CallCallback(togl, togl->ReshapeProc); } else { Togl_SetViewPort(togl); } } } #endif #if defined(TOGL_NSOPENGL) /* TODO: It appears that Tk only makes an NSView for toplevel windows. Also it looks like NSOpenGL does not have the equivalent of AGL_BUFFER_RECT that allows opengl drawing to just part of an NSView. So we might need to create our own NSView for controlling the opengl bounds. Look at TkMacOSXMakeRealWindowExist() in tkMacOSXWm.c. */ /* tell OpenGL which part of the Mac window to render to */ static void SetMacBufRect(Togl *togl) { Rect r, rt; NSRect rect; TkWindow *w = (TkWindow *) togl->TkWin; TkWindow *t = w->privatePtr->toplevel->winPtr; TkMacOSXWinBounds(w, &r); TkMacOSXWinBounds(t, &rt); rect.origin.x = r.left - rt.left; rect.origin.y = rt.bottom - r.bottom; rect.size.width = r.right - r.left; rect.size.height = r.bottom - r.top; [togl->nsview setFrame:rect]; [togl->Ctx update]; /* TODO: Support full screen. */ } static void ReconfigureCB(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *closure) { /* Display reconfiguration callback. Documented as needed by Apple QA1209. * Updated for 10.3 (and later) to use * CGDisplayRegisterReconfigurationCallback. */ Togl *togl = (Togl *) closure; if (0 != (flags & kCGDisplayBeginConfigurationFlag)) return; /* wait until display is reconfigured */ SetMacBufRect(togl); Togl_MakeCurrent(togl); if (togl->Ctx) { if (togl->ReshapeProc) { Togl_CallCallback(togl, togl->ReshapeProc); } else { Togl_SetViewPort(togl); } } } #endif /* * Called when the widget's contents must be redrawn. Basically, we * just call the user's render callback function. * * Note that the parameter type is ClientData so this function can be * passed to Tk_DoWhenIdle(). */ static void Togl_Render(ClientData clientData) { Togl *togl = (Togl *) clientData; if (togl->DisplayProc) { Togl_MakeCurrent(togl); Togl_CallCallback(togl, togl->DisplayProc); } togl->UpdatePending = False; } static void Togl_RenderOverlay(ClientData clientData) { Togl *togl = (Togl *) clientData; if (togl->OverlayFlag && togl->OverlayDisplayProc) { #if defined(TOGL_WGL) int res = wglMakeCurrent(togl->tglGLHdc, togl->tglGLOverlayHglrc); if (!res) { ErrorExit(TEXT("wglMakeCurrent overlay")); } #elif defined(TOGL_X11) (void) glXMakeCurrent(Tk_Display(togl->TkWin), togl->OverlayWindow, togl->OverlayCtx); #endif /* TOGL_WGL */ Togl_CallCallback(togl, togl->OverlayDisplayProc); } togl->OverlayUpdatePending = False; } static int Togl_EnterStereo(Togl *togl) { if (togl->Stereo == TOGL_STEREO_ROW_INTERLEAVED) { GLint stencil_bits; Tk_Window top; Togl_MakeCurrent(togl); glGetIntegerv(GL_STENCIL_BITS, &stencil_bits); if (stencil_bits == 0) { Tcl_SetResult(togl->Interp, TCL_STUPID "need stencil buffer for row interleaved stereo", TCL_STATIC); return False; } togl->riStencilBit = 1u << (stencil_bits - 1); glEnable(GL_STENCIL_TEST); /* Need to redraw window when moved between odd and even scanlines, so * bind to top level window so we're notified when that happens. */ top = togl->TkWin; while (!Tk_IsTopLevel(top)) { top = Tk_Parent(top); if (top == NULL) break; } if (top) { Tk_CreateEventHandler(top, StructureNotifyMask, Togl_RedisplayProc, (ClientData) togl); } } return True; } static void Togl_LeaveStereo(Togl *togl, int oldStereo) { switch (oldStereo) { default: break; #ifdef HAVE_AUTOSTEREO case TOGL_STEREO_NATIVE: if (togl->ash != -1) { ASClosedStereoWindow(togl->ash); togl->ash = -1; } break; #endif #ifdef __sgi case TOGL_STEREO_SGIOLDSTYLE: togl->currentStereoBuffer = STEREO_BUFFER_NONE; glXWaitGL(); /* sync with GL command stream before calling X */ XSGISetStereoBuffer(togl->display, Tk_WindowId(togl->TkWin), togl->currentStereoBuffer); glXWaitX(); /* sync with X command stream before calling GL */ break; #endif case TOGL_STEREO_ROW_INTERLEAVED: if (togl->riStencilBit) { Tk_Window top; glDisable(GL_STENCIL_TEST); /* need to remove previously added top level event handler */ top = togl->TkWin; while (!Tk_IsTopLevel(top)) { top = Tk_Parent(top); if (top == NULL) break; } if (top) { Tk_DeleteEventHandler(top, StructureNotifyMask, Togl_RedisplayProc, (ClientData) togl); } } break; } } /* * See domentation about what can't be changed */ static int Togl_ObjConfigure(Tcl_Interp *interp, Togl *togl, int objc, Tcl_Obj *const *objv) { Tk_SavedOptions savedOptions; int error, mask; int undoMask = 0; Tcl_Obj *errorResult = NULL; int oldStereo = togl->Stereo; int oldWidth = togl->Width; int oldHeight = togl->Height; for (error = 0; error <= 1; ++error, mask = undoMask) { if (error == 0) { /* * Tk_SetOptions parses the command arguments * and looks for defaults in the resource database. */ if (Tk_SetOptions(interp, WIDGREC togl, togl->tpg->optionTable, objc, objv, togl->TkWin, &savedOptions, &mask) != TCL_OK) { /* previous values are restored, so nothing to do */ return TCL_ERROR; } } else { /* * Restore options from saved values */ errorResult = Tcl_GetObjResult(interp); Tcl_IncrRefCount(errorResult); Tk_RestoreSavedOptions(&savedOptions); } if (togl->Ident && togl->Ident[0] == '.') { Tcl_AppendResult(interp, "Can not set ident to a window path name", NULL); continue; } if (togl->FullscreenFlag) { /* override width and height */ togl->Width = WidthOfScreen(Tk_Screen(togl->TkWin)); togl->Height = HeightOfScreen(Tk_Screen(togl->TkWin)); undoMask |= GEOMETRY_MASK; } if (mask & GEOMETRY_MASK) { if (!togl->PbufferFlag) { Togl_WorldChanged((ClientData) togl); /* Reset width and height so ConfigureNotify * event will call reshape callback */ togl->Width = oldWidth; togl->Height = oldHeight; undoMask |= GEOMETRY_MASK; } } if (mask & OVERLAY_MASK) { #if !TOGL_USE_OVERLAY if (togl->OverlayFlag) { Tcl_AppendResult(interp, "Sorry, overlay was disabled", NULL); continue; } #else # if defined(TOGL_X11) if (togl->OverlayCtx) # elif defined(TOGL_WGL) if (togl->tglGLOverlayHglrc) # endif { /* * Trying to change existing pixel format/graphics context */ Tcl_AppendResult(interp, "Unable to change overlay pixel format", NULL); continue; } #endif } if (mask & SWAP_MASK) { if (togl->Ctx) { /* * Change existing swap interval */ Togl_MakeCurrent(togl); /* TODO: needed? */ Togl_SwapInterval(togl, togl->SwapInterval); undoMask |= SWAP_MASK; } } if (error == 0 && (mask & STEREO_FORMAT_MASK) != 0) { if (oldStereo == TOGL_STEREO_NATIVE || togl->Stereo == TOGL_STEREO_NATIVE) { /* only native stereo affects the visual format */ mask |= FORMAT_MASK; } if (togl->Stereo == TOGL_STEREO_SGIOLDSTYLE) { #ifndef __sgi Tcl_AppendResult(interp, "sgioldstyle: only available on SGI computers", NULL); continue; #else int event, error; /* Make sure Display supports SGIStereo */ if (XSGIStereoQueryExtension(Tk_Display(togl->TkWin), &event, &error) == False) { Tcl_AppendResult(interp, "sgioldstyle: SGIStereo X extension is missing", NULL); continue; } /* Make sure Window (Screen) supports SGIStereo */ if (XSGIQueryStereoMode(Tk_Display(togl->TkWin), Tk_WindowId(Tk_Parent(togl->TkWin))) == X_STEREO_UNSUPPORTED) { Tcl_AppendResult(interp, "sgioldstyle: unsupported by screen", NULL); continue; } #endif } } if (mask & FORMAT_MASK) { if (togl->Ctx) { /* * Trying to change existing pixel format/graphics context * TODO: (re)create graphics context * * save old graphics context * try to create new one and share display lists * if failure, then restore old one */ Tcl_AppendResult(interp, "Unable to change pixel format", NULL); continue; } if (togl->ShareContext && togl->ShareList) { Tcl_AppendResult(interp, "only one of -sharelist and -sharecontext allowed", NULL); continue; } if (togl->PbufferFlag && togl->Stereo) { Tcl_AppendResult(interp, "pbuffer not supported with stereo", NULL); continue; } if (togl->PbufferFlag && togl->OverlayFlag) { Tcl_AppendResult(interp, "pbuffer not supported with overlay", NULL); continue; } if (togl->FullscreenFlag) { #if defined(TOGL_NSOPENGL) Tcl_AppendResult(interp, "Fullscreen not supported with Cocoa Tk", NULL); continue; #endif #ifndef TOGL_AGL # if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 5) Tcl_AppendResult(interp, "Need Tk 8.5 or later for fullscreen support", NULL); continue; # endif #endif } /* Whether or not the format is okay is figured out when togl tries * to create the window. */ #ifdef MESA_COLOR_HACK free_default_color_cells(Tk_Display(togl->TkWin), Tk_Colormap(togl->TkWin)); #endif undoMask |= FORMAT_MASK; } if (togl->Ctx) { if (oldStereo != togl->Stereo) { /* leaving stereo */ Togl_LeaveStereo(togl, oldStereo); if (togl->Stereo && !Togl_EnterStereo(togl)) continue; } } if (mask & TIMER_MASK) { if (togl->timerHandler != NULL) { Tcl_DeleteTimerHandler(togl->timerHandler); } if (togl->TimerProc) { togl->timerHandler = Tcl_CreateTimerHandler(togl->TimerInterval, Togl_Timer, (ClientData) togl); } undoMask |= TIMER_MASK; } break; } if (error == 0) { Tk_FreeSavedOptions(&savedOptions); return TCL_OK; } else { Tcl_SetObjResult(interp, errorResult); Tcl_DecrRefCount(errorResult); return TCL_ERROR; } } static int Togl_ObjWidget(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Togl *togl = (Togl *) clientData; const char *commands[] = { "cget", "configure", "extensions", "postredisplay", "render", "swapbuffers", "makecurrent", "takephoto", "loadbitmapfont", "unloadbitmapfont", "write", "uselayer", "showoverlay", "hideoverlay", "postredisplayoverlay", "renderoverlay", "existsoverlay", "ismappedoverlay", "getoverlaytransparentvalue", "drawbuffer", "clear", "frustum", "ortho", "numeyes", "contexttag", "copycontextto", "maketopfortrackpadevents", NULL }; enum command { TOGL_CGET, TOGL_CONFIGURE, TOGL_EXTENSIONS, TOGL_POSTREDISPLAY, TOGL_RENDER, TOGL_SWAPBUFFERS, TOGL_MAKECURRENT, TOGL_TAKEPHOTO, TOGL_LOADBITMAPFONT, TOGL_UNLOADBITMAPFONT, TOGL_WRITE, TOGL_USELAYER, TOGL_SHOWOVERLAY, TOGL_HIDEOVERLAY, TOGL_POSTREDISPLAYOVERLAY, TOGL_RENDEROVERLAY, TOGL_EXISTSOVERLAY, TOGL_ISMAPPEDOVERLAY, TOGL_GETOVERLAYTRANSPARENTVALUE, TOGL_DRAWBUFFER, TOGL_CLEAR, TOGL_FRUSTUM, TOGL_ORTHO, TOGL_NUMEYES, TOGL_CONTEXTTAG, TOGL_COPYCONTEXTTO, TOGL_MAKETOPFORTRACKPADEVENTS }; int result = TCL_OK; Tcl_Obj *objPtr; int index; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "command ?arg arg ...?"); return TCL_ERROR; } Tk_Preserve((ClientData) togl); result = Tcl_GetIndexFromObj(interp, objv[1], commands, "option", 0, &index); switch (index) { case TOGL_CGET: if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); result = TCL_ERROR; break; } objPtr = Tk_GetOptionValue(interp, WIDGREC togl, togl->tpg->optionTable, (objc == 3) ? objv[2] : NULL, togl->TkWin); if (objPtr == NULL) { result = TCL_ERROR; break; } Tcl_SetObjResult(interp, objPtr); break; case TOGL_CONFIGURE: if (objc <= 3) { /* * Return one item if the option is given, * or return all configuration information */ objPtr = Tk_GetOptionInfo(interp, WIDGREC togl, togl->tpg->optionTable, (objc == 3) ? objv[2] : NULL, togl->TkWin); if (objPtr == NULL) { result = TCL_ERROR; } else { Tcl_SetObjResult(interp, objPtr); } } else { /* Execute a configuration change */ result = Togl_ObjConfigure(interp, togl, objc - 2, objv + 2); } break; case TOGL_EXTENSIONS: /* Return a list of OpenGL extensions available */ /* TODO: -glu for glu extensions, -platform for glx/wgl extensions */ if (objc == 2) { const char *extensions; Tcl_Obj *objPtr; int length = -1; extensions = (const char *) glGetString(GL_EXTENSIONS); objPtr = Tcl_NewStringObj(extensions, -1); /* convert to list by asking for its length */ (void) Tcl_ListObjLength(interp, objPtr, &length); Tcl_SetObjResult(interp, objPtr); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_POSTREDISPLAY: /* schedule the widget to be redrawn */ if (objc == 2) { Togl_PostRedisplay(togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_RENDER: /* force the widget to be redrawn */ if (objc == 2) { Togl_Render((ClientData) togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_SWAPBUFFERS: /* force the widget to be redrawn */ if (objc == 2) { Togl_SwapBuffers(togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_MAKECURRENT: /* force the widget to be redrawn */ if (objc == 2) { Togl_MakeCurrent(togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_TAKEPHOTO: { /* force the widget to be redrawn */ if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "name"); result = TCL_ERROR; } else { const char *name; Tk_PhotoHandle photo; name = Tcl_GetStringFromObj(objv[2], NULL); photo = Tk_FindPhoto(interp, name); if (photo == NULL) { Tcl_AppendResult(interp, "image \"", name, "\" doesn't exist or is not a photo image", NULL); result = TCL_ERROR; break; } glPushAttrib(GL_PIXEL_MODE_BIT); if (togl->DoubleFlag) { glReadBuffer(GL_FRONT); } Togl_TakePhoto(togl, photo); glPopAttrib(); /* restore glReadBuffer */ } break; } case TOGL_LOADBITMAPFONT: #if TOGL_USE_FONTS != 1 Tcl_AppendResult(interp, "unsupported", NULL); result = TCL_ERROR; #else if (objc >= 3) { Tcl_Obj *font, *list; list = Tcl_NewListObj(objc - 2, objv + 2); Tcl_IncrRefCount(list); font = Togl_LoadBitmapFont(togl, Tcl_GetString(list)); Tcl_DecrRefCount(list); if (font) { Tcl_SetObjResult(interp, font); result = TCL_OK; } else { Tcl_AppendResult(interp, "Could not allocate font", NULL); result = TCL_ERROR; } } else { Tcl_WrongNumArgs(interp, 2, objv, "fontname"); result = TCL_ERROR; } #endif break; case TOGL_UNLOADBITMAPFONT: #if TOGL_USE_FONTS != 1 Tcl_AppendResult(interp, "unsupported", NULL); result = TCL_ERROR; #else if (objc == 3) { result = Togl_UnloadBitmapFont(togl, objv[2]); } else { Tcl_WrongNumArgs(interp, 2, objv, "toglfont"); result = TCL_ERROR; } #endif break; case TOGL_WRITE:{ #if TOGL_USE_FONTS != 1 Tcl_AppendResult(interp, "unsupported", NULL); result = TCL_ERROR; #else /* Tcl_Obj *toglfont = objv[2]; */ int wobjc = objc - 3; Tcl_Obj *const *wobjv = objv + 3; while (wobjc > 1) { const char *name = Tcl_GetStringFromObj(wobjv[0], NULL); int oc, i; Tcl_Obj **ov; double args[4]; if (Tcl_ListObjGetElements(NULL, wobjv[1], &oc, &ov) != TCL_OK) { oc = 0; } else if (oc <= 4) { for (i = 0; i < oc; ++i) { if (Tcl_GetDoubleFromObj(NULL, ov[i], &args[i]) != TCL_OK) { } } } if (strcmp(name, "-color") == 0) { if (oc == 4) glColor4d(args[0], args[1], args[2], args[3]); else if (oc == 3) glColor3d(args[0], args[1], args[2]); else goto write_usage; } else if (strcmp(name, "-pos") == 0) { if (oc == 4) glRasterPos4d(args[0], args[1], args[2], args[3]); else if (oc == 3) glRasterPos3d(args[0], args[1], args[2]); else if (oc == 2) glRasterPos2d(args[0], args[1]); else goto write_usage; } else goto write_usage; wobjc -= 2; wobjv += 2; } if (wobjc != 1) goto write_usage; result = Togl_WriteObj(togl, objv[2], wobjv[0]); if (result != -1) result = TCL_OK; else { Tcl_AppendResult(interp, "togl write failed", NULL); result = TCL_ERROR; } break; write_usage: Tcl_WrongNumArgs(interp, 2, objv, "[-pos {x y [z [w]]}]" " [-color {r g b [a]}" " string"); result = TCL_ERROR; #endif break; } case TOGL_USELAYER: if (objc == 3) { int layer; result = Tcl_GetIntFromObj(interp, objv[2], &layer); if (result == TCL_OK) { Togl_UseLayer(togl, layer); } } else { Tcl_WrongNumArgs(interp, 2, objv, "layer"); result = TCL_ERROR; } break; case TOGL_SHOWOVERLAY: if (objc == 2) { Togl_ShowOverlay(togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_HIDEOVERLAY: if (objc == 2) { Togl_HideOverlay(togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_POSTREDISPLAYOVERLAY: if (objc == 2) { Togl_PostOverlayRedisplay(togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_RENDEROVERLAY: /* force the overlay to be redrawn */ if (objc == 2) { Togl_RenderOverlay((ClientData) togl); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_EXISTSOVERLAY: if (objc == 2) { Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_ExistsOverlay(togl))); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_ISMAPPEDOVERLAY: if (objc == 2) { Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_IsMappedOverlay(togl))); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_GETOVERLAYTRANSPARENTVALUE: if (objc == 2) { Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_GetOverlayTransparentValue(togl))); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_DRAWBUFFER: if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "mode"); result = TCL_ERROR; } else { int mask; result = Tcl_GetIntFromObj(interp, objv[2], &mask); if (result == TCL_ERROR) break; Togl_DrawBuffer(togl, (GLenum) mask); } break; case TOGL_CLEAR: if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "mask"); result = TCL_ERROR; } else { int mask; result = Tcl_GetIntFromObj(interp, objv[2], &mask); if (result == TCL_ERROR) break; Togl_Clear(togl, (GLbitfield) mask); } break; case TOGL_FRUSTUM: if (objc != 8) { Tcl_WrongNumArgs(interp, 2, objv, "left right bottom top near far"); result = TCL_ERROR; } else { double left, right, bottom, top, zNear, zFar; if (Tcl_GetDoubleFromObj(interp, objv[2], &left) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[3], &right) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[4], &bottom) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[5], &top) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[6], &zNear) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[7], &zFar) == TCL_ERROR) { result = TCL_ERROR; break; } Togl_Frustum(togl, left, right, bottom, top, zNear, zFar); } break; case TOGL_ORTHO: if (objc != 8) { Tcl_WrongNumArgs(interp, 2, objv, "left right bottom top near far"); result = TCL_ERROR; } else { double left, right, bottom, top, zNear, zFar; if (Tcl_GetDoubleFromObj(interp, objv[2], &left) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[3], &right) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[4], &bottom) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[5], &top) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[6], &zNear) == TCL_ERROR || Tcl_GetDoubleFromObj(interp, objv[7], &zFar) == TCL_ERROR) { result = TCL_ERROR; break; } Togl_Ortho(togl, left, right, bottom, top, zNear, zFar); } break; case TOGL_NUMEYES: if (objc == 2) { Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_NumEyes(togl))); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_CONTEXTTAG: if (objc == 2) { Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_ContextTag(togl))); } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; case TOGL_COPYCONTEXTTO: if (objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } else { Togl *to; unsigned int mask; if (Togl_GetToglFromObj(togl->Interp, objv[2], &to) == TCL_ERROR || Tcl_GetIntFromObj(togl->Interp, objv[3], (int *) &mask) == TCL_ERROR) { result = TCL_ERROR; break; } result = Togl_CopyContext(togl, to, mask); } #ifdef TOGL_NSOPENGL case TOGL_MAKETOPFORTRACKPADEVENTS: if (objc == 2) { // This hack places the Togl NSView at the top of sibling views so that it receives // trackpad events. The hierarchy is not used for drawing, nor for mouse event dispatching. [togl->nsview retain]; [togl->nsview removeFromSuperview]; MacDrawable *d = ((TkWindow *) togl->TkWin)->privatePtr; NSView *topview = d->toplevel->view; [topview addSubview:togl->nsview]; [togl->nsview release]; } else { Tcl_WrongNumArgs(interp, 2, objv, NULL); result = TCL_ERROR; } break; #endif } Tk_Release((ClientData) togl); return result; } /* * Togl_ObjCmdDelete * * Called when togl command is removed from interpreter. */ static void Togl_ObjCmdDelete(ClientData clientData) { if (clientData != NULL) { Togl_PackageGlobals *tpg = (Togl_PackageGlobals *) clientData; Tk_DeleteOptionTable(tpg->optionTable); ckfree((char *) clientData); } } /* * Togl_ObjCmd * * Called when Togl is executed - creation of a Togl widget. * * Creates a new window * * Creates an 'Togl' data structure * * Creates an event handler for this window * * Creates a command that handles this object * * Configures this Togl for the given arguments */ int Togl_ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { Togl_PackageGlobals *tpg; Togl *togl; Tk_Window tkwin; Tcl_SavedResult saveError; if (objc <= 1) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); return TCL_ERROR; } tpg = (Togl_PackageGlobals *) clientData; if (tpg == NULL) { Tcl_CmdInfo info; const char *name; /* * Initialize the Togl_PackageGlobals for this widget the * first time a Togl widget is created. The globals are * saved as our client data. */ tpg = (Togl_PackageGlobals *) ckalloc(sizeof (Togl_PackageGlobals)); if (tpg == NULL) { return TCL_ERROR; } tpg->nextContextTag = 0; tpg->optionTable = Tk_CreateOptionTable(interp, optionSpecs); tpg->toglHead = NULL; name = Tcl_GetString(objv[0]); Tcl_GetCommandInfo(interp, name, &info); info.objClientData = (ClientData) tpg; Tcl_SetCommandInfo(interp, name, &info); } /* Create the window. */ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), NULL); if (tkwin == NULL) { return TCL_ERROR; } Tk_SetClass(tkwin, "Togl"); /* Create Togl data structure */ togl = (Togl *) ckalloc(sizeof (Togl)); if (togl == NULL) { return TCL_ERROR; } /* initialize Togl data structures values */ togl->Next = NULL; togl->Ctx = NULL; #if defined(TOGL_WGL) togl->tglGLHdc = NULL; togl->tglGLOverlayHglrc = NULL; #elif defined(TOGL_X11) togl->OverlayCtx = NULL; #endif togl->contextTag = 0; togl->display = Tk_Display(tkwin); togl->TkWin = tkwin; togl->Interp = interp; togl->VisInfo = NULL; togl->OverlayWindow = None; togl->OverlayCmap = None; togl->OverlayTransparentPixel = 0; togl->OverlayIsMapped = False; togl->UpdatePending = False; togl->OverlayUpdatePending = False; togl->tpg = tpg; togl->Client_Data = NULL; /* for color index mode photos */ togl->RedMap = togl->GreenMap = togl->BlueMap = NULL; togl->MapSize = 0; #ifndef NO_TK_CURSOR togl->Cursor = None; #endif togl->Width = 0; togl->Height = 0; togl->PixelScale = 1; togl->SetGrid = 0; togl->TimerInterval = 0; togl->RgbaFlag = True; togl->RgbaRed = 1; togl->RgbaGreen = 1; togl->RgbaBlue = 1; togl->DoubleFlag = False; togl->DepthFlag = False; togl->DepthSize = 1; togl->AccumFlag = False; togl->AccumRed = 1; togl->AccumGreen = 1; togl->AccumBlue = 1; togl->AccumAlpha = 1; togl->AlphaFlag = False; togl->AlphaSize = 1; togl->StencilFlag = False; togl->StencilSize = 1; togl->OverlayFlag = False; togl->Stereo = TOGL_STEREO_NONE; togl->EyeSeparation = 0; togl->Convergence = 0; togl->riStencilBit = 0; togl->AuxNumber = 0; togl->Indirect = False; togl->PixelFormat = 0; togl->SwapInterval = 1; togl->MultisampleFlag = False; togl->FullscreenFlag = False; togl->PbufferFlag = False; togl->LargestPbufferFlag = False; #if defined(TOGL_X11) togl->fbcfg = None; togl->pbuf = None; #elif defined(TOGL_WGL) togl->pbuf = None; togl->pbufferLost = 0; #elif defined(TOGL_AGL) togl->pbuf = NULL; #elif defined(TOGL_NSOPENGL) togl->pbuf = NULL; #endif togl->CreateProc = NULL; togl->DisplayProc = NULL; togl->ReshapeProc = NULL; togl->DestroyProc = NULL; togl->TimerProc = NULL; togl->timerHandler = NULL; togl->OverlayDisplayProc = NULL; togl->MagnifyProc = NULL; togl->RotateProc = NULL; togl->ScrollProc = NULL; togl->ScrollWheelProc = NULL; togl->TouchesProc = NULL; togl->ShareList = NULL; togl->ShareContext = NULL; togl->Ident = NULL; togl->PrivateCmapFlag = False; togl->currentStereoBuffer = STEREO_BUFFER_NONE; #ifdef HAVE_AUTOSTEREO togl->as_initialized = False; togl->ash = -1; #endif togl->badWindow = False; /* Create command event handler */ togl->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(tkwin), Togl_ObjWidget, (ClientData) togl, ToglCmdDeletedProc); /* * Setup the Tk_ClassProcs callbacks to point at our own window creation * function * * We need to check at runtime if we should use the new Tk_SetClassProcs() * API or if we need to modify the window structure directly */ #ifdef HAVE_TK_SETCLASSPROCS if (SetClassProcsPtr != NULL) { /* use public API (Tk 8.4+) */ Tk_ClassProcs *procsPtr; procsPtr = (Tk_ClassProcs *) ckalloc(sizeof (Tk_ClassProcs)); procsPtr->size = sizeof (Tk_ClassProcs); procsPtr->createProc = Togl_MakeWindow; procsPtr->worldChangedProc = Togl_WorldChanged; procsPtr->modalProc = NULL; /* Tk_SetClassProcs(togl->TkWin, procsPtr, (ClientData) togl); */ (SetClassProcsPtr) (togl->TkWin, procsPtr, (ClientData) togl); } else #endif { /* use private API */ /* * We need to set these fields in the Tk_FakeWin structure: dummy17 = * classProcsPtr dummy18 = instanceData */ TkClassProcs *procsPtr; Tk_FakeWin *winPtr = (Tk_FakeWin *) (togl->TkWin); procsPtr = (TkClassProcs *) ckalloc(sizeof (TkClassProcs)); procsPtr->createProc = Togl_MakeWindow; procsPtr->geometryProc = Togl_WorldChanged; procsPtr->modalProc = NULL; winPtr->dummy17 = (char *) procsPtr; winPtr->dummy18 = (ClientData) togl; } Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask, Togl_EventProc, (ClientData) togl); /* Configure Togl widget */ if (Tk_InitOptions(interp, WIDGREC togl, tpg->optionTable, tkwin) != TCL_OK || Togl_ObjConfigure(interp, togl, objc - 2, objv + 2) != TCL_OK) { goto error; } /* * If OpenGL window wasn't already created by Togl_ObjConfigure() we * create it now. We can tell by checking if the OpenGL context has * been initialized. */ if (!togl->Ctx) { Tk_MakeWindowExist(togl->TkWin); if (togl->badWindow) { goto error; } } Togl_MakeCurrent(togl); if (togl->contextTag == 0) togl->contextTag = ++tpg->nextContextTag; (void) Togl_SwapInterval(togl, togl->SwapInterval); /* If defined, call create callback */ if (togl->CreateProc) { if (Togl_CallCallback(togl, togl->CreateProc) != TCL_OK) { goto error; } } #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) if (!togl->PbufferFlag) SetMacBufRect(togl); #endif /* If defined, call reshape proc */ if (togl->ReshapeProc) { if (Togl_CallCallback(togl, togl->ReshapeProc) != TCL_OK) { goto error; } } else { Togl_SetViewPort(togl); #if defined(TOGL_X11) if (togl->OverlayFlag) { Togl_UseLayer(togl, TOGL_OVERLAY); Togl_SetViewPort(togl); Togl_UseLayer(togl, TOGL_NORMAL); } #endif } if (togl->Stereo && !Togl_EnterStereo(togl)) goto error; Tcl_AppendResult(interp, Tk_PathName(tkwin), NULL); /* Add to linked list */ AddToList(togl); return TCL_OK; error: Tcl_SaveResult(interp, &saveError); togl->badWindow = True; (void) Tcl_DeleteCommandFromToken(interp, togl->widgetCmd); Tcl_RestoreResult(interp, &saveError); Tcl_AppendResult(interp, "\nCouldn't configure togl widget", NULL); return TCL_ERROR; } #if TOGL_USE_OVERLAY /* * Do all the setup for overlay planes * Return: TCL_OK or TCL_ERROR */ static int SetupOverlay(Togl *togl) { # if defined(TOGL_X11) # ifdef GLX_TRANSPARENT_TYPE_EXT static int ovAttributeList[] = { GLX_BUFFER_SIZE, 2, GLX_LEVEL, 1, GLX_TRANSPARENT_TYPE_EXT, GLX_TRANSPARENT_INDEX_EXT, None }; # else static int ovAttributeList[] = { GLX_BUFFER_SIZE, 2, GLX_LEVEL, 1, None }; # endif XVisualInfo *visinfo; TkWindow *winPtr = (TkWindow *) togl->TkWin; XSetWindowAttributes swa; Tcl_HashEntry *hPtr; int new_flag; visinfo = glXChooseVisual(togl->display, Tk_ScreenNumber(winPtr), ovAttributeList); if (!visinfo) { Tcl_AppendResult(togl->Interp, Tk_PathName(winPtr), ": No suitable overlay index visual available", NULL); togl->OverlayCtx = NULL; togl->OverlayWindow = 0; togl->OverlayCmap = 0; return TCL_ERROR; } # ifdef GLX_TRANSPARENT_INDEX_EXT { int fail = glXGetConfig(togl->display, visinfo, GLX_TRANSPARENT_INDEX_VALUE_EXT, &togl->OverlayTransparentPixel); if (fail) togl->OverlayTransparentPixel = 0; /* maybe, maybe ... */ } # else togl->OverlayTransparentPixel = 0; /* maybe, maybe ... */ # endif /* share display lists with normal layer context */ togl->OverlayCtx = glXCreateContext(togl->display, visinfo, togl->Ctx, !togl->Indirect); swa.colormap = XCreateColormap(togl->display, XRootWindow(togl->display, visinfo->screen), visinfo->visual, AllocNone); togl->OverlayCmap = swa.colormap; swa.border_pixel = 0; swa.event_mask = ALL_EVENTS_MASK; togl->OverlayWindow = XCreateWindow(togl->display, Tk_WindowId(togl->TkWin), 0, 0, togl->Width, togl->Height, 0, visinfo->depth, InputOutput, visinfo->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); hPtr = Tcl_CreateHashEntry(&winPtr->dispPtr->winTable, (const char *) togl->OverlayWindow, &new_flag); Tcl_SetHashValue(hPtr, winPtr); /* XMapWindow(togl->display, togl->OverlayWindow); */ togl->OverlayIsMapped = False; /* Make sure window manager installs our colormap */ XSetWMColormapWindows(togl->display, togl->OverlayWindow, &togl->OverlayWindow, 1); return TCL_OK; # elif defined(TOGL_WGL) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL) /* not yet implemented on these */ return TCL_ERROR; # endif } #endif /* TOGL_USE_OVERLAY */ #ifdef TOGL_WGL # define TOGL_CLASS_NAME "Togl Class" static Bool ToglClassInitialized = False; static LRESULT CALLBACK Win32WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LONG result; Togl *togl = (Togl *) GetWindowLongPtr(hwnd, 0); WNDCLASS childClass; switch (message) { case WM_WINDOWPOSCHANGED: /* Should be processed by DefWindowProc, otherwise a double buffered * context is not properly resized when the corresponding window is * resized. */ break; case WM_DESTROY: if (togl && togl->TkWin != NULL) { if (togl->SetGrid > 0) { Tk_UnsetGrid(togl->TkWin); } (void) Tcl_DeleteCommandFromToken(togl->Interp, togl->widgetCmd); } break; case WM_ERASEBKGND: /* We clear our own window */ return 1; case WM_DISPLAYCHANGE: if (togl->PbufferFlag && hasARBPbuffer && !togl->pbufferLost) { queryPbuffer(togl->pbuf, WGL_PBUFFER_LOST_ARB, &togl->pbufferLost); } /* FALLTHROUGH */ default: # if USE_STATIC_LIB return TkWinChildProc(hwnd, message, wParam, lParam); # else /* * OK, since TkWinChildProc is not explicitly exported in the * dynamic libraries, we have to retrieve it from the class info * registered with windows. * */ if (tkWinChildProc == NULL) { GetClassInfo(Tk_GetHINSTANCE(), TK_WIN_CHILD_CLASS_NAME, &childClass); tkWinChildProc = childClass.lpfnWndProc; } return tkWinChildProc(hwnd, message, wParam, lParam); # endif } result = DefWindowProc(hwnd, message, wParam, lParam); Tcl_ServiceAll(); return result; } #endif /* TOGL_WGL */ /* * Togl_MakeWindow * * Window creation function, invoked as a callback from Tk_MakeWindowExist. * This is called instead of TkpMakeWindow and must always succeed. */ static Window Togl_MakeWindow(Tk_Window tkwin, Window parent, ClientData instanceData) { Togl *togl = (Togl *) instanceData; Display *dpy; Colormap cmap; int scrnum; Window window = None; #if defined(TOGL_X11) Bool directCtx = True; XSetWindowAttributes swa; int width, height; #elif defined(TOGL_WGL) HWND hwnd, parentWin; DWORD style; HINSTANCE hInstance; PIXELFORMATDESCRIPTOR pfd; int width, height; Bool createdPbufferDC = False; #elif defined(TOGL_AGL) #endif if (togl->badWindow) { TkWindow *winPtr = (TkWindow *) tkwin; return TkpMakeWindow(winPtr, parent); } /* for color index mode photos */ if (togl->RedMap) free(togl->RedMap); if (togl->GreenMap) free(togl->GreenMap); if (togl->BlueMap) free(togl->BlueMap); togl->RedMap = togl->GreenMap = togl->BlueMap = NULL; togl->MapSize = 0; dpy = Tk_Display(tkwin); scrnum = Tk_ScreenNumber(tkwin); /* * Windows and Mac OS X need the window created before OpenGL context * is created. So do that now and set the window variable. */ #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) { TkWindow *winPtr = (TkWindow *) tkwin; window = TkpMakeWindow(winPtr, parent); if (!togl->PbufferFlag) (void) XMapWindow(dpy, window); } #elif defined(TOGL_WGL) hInstance = Tk_GetHINSTANCE(); if (!ToglClassInitialized) { WNDCLASS ToglClass; ToglClassInitialized = True; ToglClass.style = CS_HREDRAW | CS_VREDRAW; ToglClass.cbClsExtra = 0; ToglClass.cbWndExtra = sizeof (LONG_PTR); /* to save Togl* */ ToglClass.hInstance = hInstance; ToglClass.hbrBackground = NULL; ToglClass.lpszMenuName = NULL; ToglClass.lpszClassName = TOGL_CLASS_NAME; ToglClass.lpfnWndProc = Win32WinProc; ToglClass.hIcon = NULL; ToglClass.hCursor = NULL; if (!RegisterClass(&ToglClass)) { Tcl_SetResult(togl->Interp, TCL_STUPID "unable register Togl window class", TCL_STATIC); goto error; } } /* duplicate tkpMakeWindow logic from tk8.[45]/win/tkWinWindow.c */ if (parent != None) { parentWin = Tk_GetHWND(parent); style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; } else { parentWin = NULL; style = WS_POPUP | WS_CLIPCHILDREN; } if (togl->PbufferFlag) { width = height = 1; /* TODO: demo code mishaves when set to 1000 */ } else { width = togl->Width; height = togl->Height; } hwnd = CreateWindowEx(WS_EX_NOPARENTNOTIFY, TOGL_CLASS_NAME, NULL, style, 0, 0, width, height, parentWin, NULL, hInstance, NULL); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); window = Tk_AttachHWND(tkwin, hwnd); SetWindowLongPtr(hwnd, 0, (LONG_PTR) togl); if (togl->PbufferFlag) { ShowWindow(hwnd, SW_HIDE); /* make sure it's hidden */ } #endif /* * Figure out which OpenGL context to use */ #ifdef TOGL_WGL togl->tglGLHdc = GetDC(hwnd); #endif if (togl->PixelFormat) { #if defined(TOGL_X11) XVisualInfo template; int count = 0; template.visualid = togl->PixelFormat; togl->VisInfo = XGetVisualInfo(dpy, VisualIDMask, &template, &count); if (togl->VisInfo == NULL) { Tcl_SetResult(togl->Interp, TCL_STUPID "missing visual information", TCL_STATIC); goto error; } #endif if (!togl_describePixelFormat(togl)) { Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose pixel format", TCL_STATIC); goto error; } } else { #if defined(TOGL_X11) togl->VisInfo = togl_pixelFormat(togl, scrnum); if (togl->VisInfo == NULL) #elif defined(TOGL_WGL) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL) # ifdef TOGL_WGL togl->PixelFormat = togl_pixelFormat(togl, hwnd); # elif defined(TOGL_NSOPENGL) togl->PixelFormat = (void *)togl_pixelFormat(togl); # else togl->PixelFormat = (Tcl_WideInt)togl_pixelFormat(togl); # endif if (togl->PixelFormat == 0) #endif { goto error; } } #ifdef TOGL_WGL if (togl->PbufferFlag) { togl->pbuf = togl_createPbuffer(togl); if (togl->pbuf == NULL) { Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't create pbuffer", TCL_STATIC); goto error; } ReleaseDC(hwnd, togl->tglGLHdc); togl->tglGLHdc = getPbufferDC(togl->pbuf); createdPbufferDC = True; } else if (SetPixelFormat(togl->tglGLHdc, (int) togl->PixelFormat, NULL) == FALSE) { Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't set pixel format", TCL_STATIC); goto error; } #endif #if defined(TOGL_WGL) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL) if (togl->VisInfo == NULL) { /* * Create a new OpenGL rendering context. And check to share lists. */ Visual *visual; /* Just for portability, define the simplest visinfo */ visual = DefaultVisual(dpy, scrnum); togl->VisInfo = (XVisualInfo *) calloc(1, sizeof (XVisualInfo)); togl->VisInfo->screen = scrnum; togl->VisInfo->visual = visual; togl->VisInfo->visualid = visual->visualid; # if defined(__cplusplus) || defined(c_plusplus) togl->VisInfo->c_class = visual->c_class; # else togl->VisInfo->class = visual->class; # endif togl->VisInfo->depth = visual->bits_per_rgb; } #endif #if defined(TOGL_X11) if (togl->Indirect) { directCtx = False; } #endif /* * Create a new OpenGL rendering context. */ #if defined(TOGL_X11) if (togl->ShareList) { /* share display lists with existing togl widget */ Togl *shareWith = FindTogl(togl, togl->ShareList); GLXContext shareCtx; int error_code; if (shareWith) { shareCtx = shareWith->Ctx; togl->contextTag = shareWith->contextTag; } else { shareCtx = None; } if (shareCtx) { togl_SetupXErrorHandler(); } togl->Ctx = glXCreateContext(dpy, togl->VisInfo, shareCtx, directCtx); if (shareCtx && (error_code = togl_CheckForXError(togl))) { char buf[256]; togl->Ctx = NULL; XGetErrorText(dpy, error_code, buf, sizeof buf); Tcl_AppendResult(togl->Interp, "unable to share display lists: ", buf, NULL); goto error; } } else { if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) { /* share OpenGL context with existing Togl widget */ Togl *shareWith = FindTogl(togl, togl->ShareContext); if (togl->VisInfo->visualid != shareWith->VisInfo->visualid) { Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share OpenGL context", TCL_STATIC); goto error; } togl->Ctx = shareWith->Ctx; } else { /* don't share display lists */ togl->ShareContext = False; togl->Ctx = glXCreateContext(dpy, togl->VisInfo, None, directCtx); } } #elif defined(TOGL_WGL) if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) { /* share OpenGL context with existing Togl widget */ Togl *shareWith = FindTogl(togl, togl->ShareContext); if (togl->PixelFormat != shareWith->PixelFormat) { Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share OpenGL context", TCL_STATIC); goto error; } togl->Ctx = shareWith->Ctx; } else { togl->Ctx = wglCreateContext(togl->tglGLHdc); } if (togl->ShareList) { /* share display lists with existing togl widget */ Togl *shareWith = FindTogl(togl, togl->ShareList); if (shareWith) { if (!wglShareLists(shareWith->Ctx, togl->Ctx)) { # if 0 LPVOID lpMsgBuf; DWORD err = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); fprintf(stderr, "unable to share display lists: %d: %s\n", err, lpMsgBuf); LocalFree(lpMsgBuf); # endif Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share display lists", TCL_STATIC); goto error; } togl->contextTag = shareWith->contextTag; } } #elif defined(TOGL_AGL) AGLContext shareCtx = NULL; if (togl->ShareList) { /* share display lists with existing togl widget */ Togl *shareWith = FindTogl(togl, togl->ShareList); if (shareWith) { shareCtx = shareWith->Ctx; togl->contextTag = shareWith->contextTag; } } if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) { /* share OpenGL context with existing Togl widget */ Togl *shareWith = FindTogl(togl, togl->ShareContext); if (togl->PixelFormat != shareWith->PixelFormat) { Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share OpenGL context", TCL_STATIC); goto error; } togl->Ctx = shareWith->Ctx; } else if ((togl->Ctx = aglCreateContext((AGLPixelFormat) togl->PixelFormat, shareCtx)) == NULL) { GLenum err = aglGetError(); aglDestroyPixelFormat((AGLPixelFormat) togl->PixelFormat); togl->PixelFormat = 0; if (err == AGL_BAD_MATCH) Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share display lists" ": shared context doesn't match", TCL_STATIC); else if (err == AGL_BAD_CONTEXT) Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share display lists" ": bad shared context", TCL_STATIC); else if (err == AGL_BAD_PIXELFMT) Tcl_SetResult(togl->Interp, TCL_STUPID "could not create rendering context" ": bad pixel format", TCL_STATIC); else Tcl_SetResult(togl->Interp, TCL_STUPID "could not create rendering context" ": unknown reason", TCL_STATIC); goto error; } if (!togl->PbufferFlag && !aglSetDrawable(togl->Ctx, Togl_MacOSXGetDrawablePort(togl))) { /* aglSetDrawable is deprecated in OS X 10.5 */ aglDestroyContext(togl->Ctx); togl->Ctx = NULL; aglDestroyPixelFormat((AGLPixelFormat) togl->PixelFormat); togl->PixelFormat = 0; Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't set drawable", TCL_STATIC); goto error; } #elif defined(TOGL_NSOPENGL) NSOpenGLContext *shareCtx = NULL; if (togl->ShareList) { /* share display lists with existing togl widget */ Togl *shareWith = FindTogl(togl, togl->ShareList); if (shareWith) { shareCtx = shareWith->Ctx; togl->contextTag = shareWith->contextTag; } } if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) { /* share OpenGL context with existing Togl widget */ Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share NSOpenGL context", TCL_STATIC); goto error; /* Togl *shareWith = FindTogl(togl, togl->ShareContext); if (togl->PixelFormat != shareWith->PixelFormat) { Tcl_SetResult(togl->Interp, TCL_STUPID "unable to share OpenGL context", TCL_STATIC); goto error; } togl->Ctx = [[NSOpenGLContext alloc] initWithCGLContextObj:shareWith->Ctx]; */ /* initWithCGLContextObj requires Mac OS 10.6 */ } else { togl->Ctx = [NSOpenGLContext alloc]; if ([togl->Ctx initWithFormat:togl->PixelFormat shareContext:shareCtx] == nil) { [togl->PixelFormat release]; togl->PixelFormat = 0; Tcl_SetResult(togl->Interp, TCL_STUPID "Could not obtain OpenGL context", TCL_STATIC); goto error; } } if (!togl->PbufferFlag) { togl->nsview = [[ToglNSView alloc] initWithFrame:NSZeroRect]; [togl->nsview setTogl:togl]; if ([togl->nsview respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { [togl->nsview setWantsBestResolutionOpenGLSurface:YES]; } [togl->nsview setAcceptsTouchEvents:YES]; MacDrawable *d = ((TkWindow *) togl->TkWin)->privatePtr; NSView *topview = d->toplevel->view; [topview addSubview:togl->nsview]; /* TODO: Appears setView has to be deferred until window mapped. * or it gives "invalid drawable" error. But MapNotify doesn't happen. * I think toplevel is already mapped. Iconifying and uniconifying * main window makes the graphics work. */ /* [togl->Ctx setView:togl->nsview];*/ } #endif if (togl->Ctx == NULL) { Tcl_SetResult(togl->Interp, TCL_STUPID "could not create rendering context", TCL_STATIC); goto error; } #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) CGDisplayRegisterReconfigurationCallback(ReconfigureCB, togl); #endif if (togl->PbufferFlag) { /* Don't need a colormap, nor overlay, nor be displayed */ #if defined(TOGL_X11) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL) togl->pbuf = togl_createPbuffer(togl); if (!togl->pbuf) { /* tcl result set in togl_createPbuffer */ # ifdef TOGL_AGL if (!togl->ShareContext) { aglDestroyContext(togl->Ctx); aglDestroyPixelFormat((AGLPixelFormat) togl->PixelFormat); } togl->Ctx = NULL; togl->PixelFormat = 0; # endif # ifdef TOGL_NSOPENGL if (!togl->ShareContext) { [togl->Ctx release]; [togl->PixelFormat release]; } togl->Ctx = NULL; togl->PixelFormat = 0; # endif goto error; } # ifdef TOGL_X11 window = TkpMakeWindow((TkWindow *) tkwin, parent); # endif #endif return window; } #ifdef TOGL_WGL DescribePixelFormat(togl->tglGLHdc, (int) togl->PixelFormat, sizeof (pfd), &pfd); #endif /* * find a colormap */ if (togl->RgbaFlag) { /* Colormap for RGB mode */ #if defined(TOGL_X11) cmap = get_rgb_colormap(dpy, scrnum, togl->VisInfo, tkwin); #elif defined(TOGL_WGL) if (pfd.dwFlags & PFD_NEED_PALETTE) { cmap = Win32CreateRgbColormap(pfd); } else { cmap = DefaultColormap(dpy, scrnum); } #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL) cmap = DefaultColormap(dpy, scrnum); #endif } else { /* Colormap for CI mode */ #ifdef TOGL_WGL /* this logic is to overcome a combination driver/compiler bug: (1) * cColorBits may be unusually large (e.g., 32 instead of 8 or 12) and * (2) 1 << 32 might be 1 instead of zero (gcc for ia32) */ if (pfd.cColorBits >= MAX_CI_COLORMAP_BITS) { togl->CiColormapSize = MAX_CI_COLORMAP_SIZE; } else { togl->CiColormapSize = 1 << pfd.cColorBits; if (togl->CiColormapSize >= MAX_CI_COLORMAP_SIZE) togl->CiColormapSize = MAX_CI_COLORMAP_SIZE; } #endif if (togl->PrivateCmapFlag) { /* need read/write colormap so user can store own color entries */ #if defined(TOGL_X11) cmap = XCreateColormap(dpy, XRootWindow(dpy, togl->VisInfo->screen), togl->VisInfo->visual, AllocAll); #elif defined(TOGL_WGL) cmap = Win32CreateCiColormap(togl); #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL) /* need to figure out how to do this correctly on Mac... */ cmap = DefaultColormap(dpy, scrnum); #endif } else { if (togl->VisInfo->visual == DefaultVisual(dpy, scrnum)) { /* share default/root colormap */ cmap = Tk_Colormap(tkwin); } else { /* make a new read-only colormap */ cmap = XCreateColormap(dpy, XRootWindow(dpy, togl->VisInfo->screen), togl->VisInfo->visual, AllocNone); } } } /* Make sure Tk knows to switch to the new colormap when the cursor is over * this window when running in color index mode. */ (void) Tk_SetWindowVisual(tkwin, togl->VisInfo->visual, togl->VisInfo->depth, cmap); #ifdef TOGL_WGL /* Install the colormap */ SelectPalette(togl->tglGLHdc, ((TkWinColormap *) cmap)->palette, TRUE); RealizePalette(togl->tglGLHdc); #endif #if defined(TOGL_X11) swa.background_pixmap = None; swa.border_pixel = 0; swa.colormap = cmap; swa.event_mask = ALL_EVENTS_MASK; if (togl->PbufferFlag) { width = height = 1; } else { width = togl->Width; height = togl->Height; } window = XCreateWindow(dpy, parent, 0, 0, width, height, 0, togl->VisInfo->depth, InputOutput, togl->VisInfo->visual, CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask, &swa); /* Make sure window manager installs our colormap */ (void) XSetWMColormapWindows(dpy, window, &window, 1); if (!togl->DoubleFlag) { int dbl_flag; /* See if we requested single buffering but had to accept a double * buffered visual. If so, set the GL draw buffer to be the front * buffer to simulate single buffering. */ if (glXGetConfig(dpy, togl->VisInfo, GLX_DOUBLEBUFFER, &dbl_flag)) { if (dbl_flag) { glXMakeCurrent(dpy, window, togl->Ctx); glDrawBuffer(GL_FRONT); glReadBuffer(GL_FRONT); } } } #elif defined(TOGL_WGL) if (!togl->DoubleFlag) { /* See if we requested single buffering but had to accept a double * buffered visual. If so, set the GL draw buffer to be the front * buffer to simulate single buffering. */ if (getPixelFormatAttribiv == NULL) { /* pfd is already set */ if ((pfd.dwFlags & PFD_DOUBLEBUFFER) != 0) { wglMakeCurrent(togl->tglGLHdc, togl->Ctx); glDrawBuffer(GL_FRONT); glReadBuffer(GL_FRONT); } } else { static int attribs[] = { WGL_DOUBLE_BUFFER_ARB, }; # define NUM_ATTRIBS (sizeof attribs / sizeof attribs[0]) int info[NUM_ATTRIBS]; getPixelFormatAttribiv(togl->tglGLHdc, (int) togl->PixelFormat, 0, NUM_ATTRIBS, attribs, info); # undef NUM_ATTRIBS if (info[0]) { wglMakeCurrent(togl->tglGLHdc, togl->Ctx); glDrawBuffer(GL_FRONT); glReadBuffer(GL_FRONT); } } } #endif #if TOGL_USE_OVERLAY if (togl->OverlayFlag) { if (SetupOverlay(togl) == TCL_ERROR) { fprintf(stderr, "Warning: couldn't setup overlay.\n"); togl->OverlayFlag = False; } } #endif #if !defined(TOGL_AGL) /* Request the X window to be displayed */ (void) XMapWindow(dpy, window); #endif if (!togl->RgbaFlag) { int index_size; #if defined(TOGL_X11) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL) GLint index_bits; glGetIntegerv(GL_INDEX_BITS, &index_bits); index_size = 1 << index_bits; #elif defined(TOGL_WGL) index_size = togl->CiColormapSize; #endif if (togl->MapSize != index_size) { if (togl->RedMap) free(togl->RedMap); if (togl->GreenMap) free(togl->GreenMap); if (togl->BlueMap) free(togl->BlueMap); togl->MapSize = index_size; togl->RedMap = (GLfloat *) calloc(index_size, sizeof (GLfloat)); togl->GreenMap = (GLfloat *) calloc(index_size, sizeof (GLfloat)); togl->BlueMap = (GLfloat *) calloc(index_size, sizeof (GLfloat)); } } #ifdef HAVE_AUTOSTEREO if (togl->Stereo == TOGL_STEREO_NATIVE) { if (!togl->as_initialized) { const char *autostereod; togl->as_initialized = True; if ((autostereod = getenv("AUTOSTEREOD")) == NULL) autostereod = AUTOSTEREOD; if (autostereod && *autostereod) { if (ASInitialize(togl->display, autostereod) == Success) { togl->ash = ASCreatedStereoWindow(dpy); } } } else { togl->ash = ASCreatedStereoWindow(dpy); } } #endif return window; error: togl->badWindow = True; #if defined(TOGL_X11) if (window == None) { TkWindow *winPtr = (TkWindow *) tkwin; window = TkpMakeWindow(winPtr, parent); } #elif defined(TOGL_WGL) if (togl->tglGLHdc) { if (createdPbufferDC) releasePbufferDC(togl->pbuf, togl->tglGLHdc); else ReleaseDC(hwnd, togl->tglGLHdc); togl->tglGLHdc = NULL; } #endif return window; } /* * Togl_WorldChanged * * Add support for setgrid option. */ static void Togl_WorldChanged(ClientData instanceData) { Togl *togl = (Togl *) instanceData; int width; int height; if (togl->PbufferFlag) width = height = 1; else { width = togl->Width; height = togl->Height; } Tk_GeometryRequest(togl->TkWin, width, height); Tk_SetInternalBorder(togl->TkWin, 0); if (togl->SetGrid > 0) { Tk_SetGrid(togl->TkWin, width / togl->SetGrid, height / togl->SetGrid, togl->SetGrid, togl->SetGrid); } else { Tk_UnsetGrid(togl->TkWin); } } static void Togl_SetViewPort(const struct Togl *togl) { glViewport(0, 0, togl->Width*togl->PixelScale, togl->Height*togl->PixelScale); } /* * ToglFree * * Wrap the ckfree macro. */ static void ToglFree(char *clientData) { ckfree(clientData); } /* * ToglCmdDeletedProc * * This procedure is invoked when a widget command is deleted. If * the widget isn't already in the process of being destroyed, * this command destroys it. * * Results: * None. * * Side effects: * The widget is destroyed. * *---------------------------------------------------------------------- */ static void ToglCmdDeletedProc(ClientData clientData) { Togl *togl = (Togl *) clientData; Tk_Window tkwin = togl->TkWin; /* * This procedure could be invoked either because the window was * destroyed and the command was then deleted (in which case tkwin * is NULL) or because the command was deleted, and then this procedure * destroys the widget. */ if (tkwin) { Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask, Togl_EventProc, (ClientData) togl); } Tk_Preserve((ClientData) togl); Tcl_EventuallyFree((ClientData) togl, ToglFree); Togl_LeaveStereo(togl, togl->Stereo); if (togl->DestroyProc) { /* call user's cleanup code */ Togl_CallCallback(togl, togl->DestroyProc); } if (togl->TimerProc != NULL) { Tcl_DeleteTimerHandler(togl->timerHandler); togl->timerHandler = NULL; } if (togl->UpdatePending) { Tcl_CancelIdleCall(Togl_Render, (ClientData) togl); togl->UpdatePending = False; } #ifndef NO_TK_CURSOR if (togl->Cursor != None) { Tk_FreeCursor(togl->display, togl->Cursor); togl->Cursor = None; } #endif /* remove from linked list */ RemoveFromList(togl); togl->TkWin = NULL; if (tkwin != NULL) { if (togl->Ctx) { if (FindToglWithSameContext(togl) == NULL) { #if defined(TOGL_X11) glXDestroyContext(togl->display, togl->Ctx); #elif defined(TOGL_WGL) wglDeleteContext(togl->Ctx); #elif defined(TOGL_AGL) aglDestroyContext(togl->Ctx); CGDisplayRemoveReconfigurationCallback(ReconfigureCB, togl); #elif defined(TOGL_NSOPENGL) [togl->Ctx release]; togl->Ctx = nil; [togl->nsview setTogl:nil]; [togl->nsview release]; togl->nsview = nil; CGDisplayRemoveReconfigurationCallback(ReconfigureCB, togl); #endif #if defined(TOGL_X11) XFree(togl->VisInfo); #else free(togl->VisInfo); #endif } #if defined(TOGL_WGL) if (togl->tglGLHdc) { if (togl->PbufferFlag) { releasePbufferDC(togl->pbuf, togl->tglGLHdc); } else { HWND hwnd = Tk_GetHWND(Tk_WindowId(tkwin)); ReleaseDC(hwnd, togl->tglGLHdc); } togl->tglGLHdc = NULL; } #endif if (togl->PbufferFlag && togl->pbuf) { togl_destroyPbuffer(togl); togl->pbuf = 0; } togl->Ctx = NULL; togl->VisInfo = NULL; } #if defined(TOGL_X11) # if TOGL_USE_OVERLAY if (togl->OverlayCtx) { Tcl_HashEntry *entryPtr; TkWindow *winPtr = (TkWindow *) tkwin; if (winPtr) { entryPtr = Tcl_FindHashEntry(&winPtr->dispPtr->winTable, (const char *) togl->OverlayWindow); Tcl_DeleteHashEntry(entryPtr); } if (FindToglWithSameOverlayContext(togl) == NULL) glXDestroyContext(togl->display, togl->OverlayCtx); togl->OverlayCtx = NULL; } # endif /* TOGL_USE_OVERLAY */ #endif if (togl->SetGrid > 0) { Tk_UnsetGrid(tkwin); } Tk_DestroyWindow(tkwin); } Tk_Release((ClientData) togl); } /* * This gets called to track top level position changes for * row interleaved stereo. */ static void Togl_RedisplayProc(ClientData clientData, XEvent *eventPtr) { Togl *togl = (Togl *) clientData; switch (eventPtr->type) { case ConfigureNotify: Togl_PostRedisplay(togl); break; } } #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) static int viewPixelScale(NSView *nsview) { int pixelScale = 1; if ([nsview respondsToSelector:@selector(convertRectToBacking:)]) { NSRect wbounds = [nsview bounds]; NSRect gbounds = [nsview convertRectToBacking:wbounds]; pixelScale = (wbounds.size.width > 0 ? gbounds.size.width / wbounds.size.width : 1); } return pixelScale; } #endif /* * This gets called to handle Togl window configuration events */ static void Togl_EventProc(ClientData clientData, XEvent *eventPtr) { Togl *togl = (Togl *) clientData; switch (eventPtr->type) { case Expose: #if defined(TOGL_NSOPENGL) if (!Tk_IsMapped(togl->TkWin)) /* Tk Cocoa generates expose events for unmapped windows! */ break; #endif if (eventPtr->xexpose.count == 0) { if (!togl->UpdatePending && eventPtr->xexpose.window == Tk_WindowId(togl->TkWin)) { Togl_PostRedisplay(togl); } #if defined(TOGL_X11) if (!togl->OverlayUpdatePending && togl->OverlayFlag && togl->OverlayIsMapped && eventPtr->xexpose.window == togl->OverlayWindow) { Togl_PostOverlayRedisplay(togl); } #endif #if defined(TOGL_NSOPENGL) [togl->Ctx setView:togl->nsview]; SetMacBufRect(togl); #endif } break; case ConfigureNotify: if (togl->PbufferFlag) break; #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) int pixelScale = viewPixelScale(togl->nsview); if (togl->Width == Tk_Width(togl->TkWin) && togl->Height == Tk_Height(togl->TkWin) && togl->PixelScale == pixelScale) { // Even though the size hasn't changed, // it's position on the screen may have. if (Tk_IsMapped(togl->TkWin)) SetMacBufRect(togl); break; } #endif togl->Width = Tk_Width(togl->TkWin); togl->Height = Tk_Height(togl->TkWin); #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) togl->PixelScale = pixelScale; #endif (void) XResizeWindow(Tk_Display(togl->TkWin), Tk_WindowId(togl->TkWin), togl->Width, togl->Height); #if defined(TOGL_X11) if (togl->OverlayFlag) { (void) XResizeWindow(Tk_Display(togl->TkWin), togl->OverlayWindow, togl->Width, togl->Height); (void) XRaiseWindow(Tk_Display(togl->TkWin), togl->OverlayWindow); } #endif #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL) SetMacBufRect(togl); #endif Togl_MakeCurrent(togl); if (togl->ReshapeProc) { Togl_SetViewPort(togl); (void) Togl_CallCallback(togl, togl->ReshapeProc); } else { Togl_SetViewPort(togl); #if defined(TOGL_X11) if (togl->OverlayFlag) { Togl_UseLayer(togl, TOGL_OVERLAY); Togl_SetViewPort(togl); Togl_UseLayer(togl, TOGL_NORMAL); } #endif } break; case MapNotify: #if defined(TOGL_AGL) if (!togl->PbufferFlag) { /* * See comment for the UnmapNotify case below. */ AGLDrawable d = Togl_MacOSXGetDrawablePort(togl); /* aglSetDrawable is deprecated in OS X 10.5 */ aglSetDrawable(togl->Ctx, d); SetMacBufRect(togl); } #endif #if defined(TOGL_NSOPENGL) if (!togl->PbufferFlag) { /* * See comment for the UnmapNotify case below. */ [togl->Ctx setView:togl->nsview]; SetMacBufRect(togl); } #endif break; case UnmapNotify: #if defined(TOGL_AGL) if (!togl->PbufferFlag) { /* * For Mac OS X Aqua, Tk subwindows are not implemented as * separate Aqua windows. They are just different regions of * a single Aqua window. To unmap them they are just not drawn. * Have to disconnect the AGL context otherwise they will continue * to be displayed directly by Aqua. */ /* aglSetDrawable is deprecated in OS X 10.5 */ aglSetDrawable(togl->Ctx, NULL); } #endif #if defined(TOGL_NSOPENGL) if (!togl->PbufferFlag) { /* * For Mac OS X Aqua, Tk subwindows are not implemented as * separate Aqua windows. They are just different regions of * a single Aqua window. To unmap them they are just not drawn. * Have to disconnect the NSView otherwise they will continue * to be displayed directly by Aqua. */ [togl->Ctx clearDrawable]; } #endif break; case DestroyNotify: if (togl->TkWin != NULL) { #ifdef TOGL_WGL HWND hwnd = Tk_GetHWND(Tk_WindowId(togl->TkWin)); /* Prevent Win32WinProc from calling Tcl_DeleteCommandFromToken * a second time */ SetWindowLongPtr(hwnd, 0, (LONG_PTR) 0); #endif if (togl->SetGrid > 0) { Tk_UnsetGrid(togl->TkWin); } (void) Tcl_DeleteCommandFromToken(togl->Interp, togl->widgetCmd); } break; default: /* nothing */ ; } } void Togl_PostRedisplay(Togl *togl) { if (!togl->UpdatePending) { togl->UpdatePending = True; Tk_DoWhenIdle(Togl_Render, (ClientData) togl); } } Bool Togl_UpdatePending(const Togl *togl) { return togl->UpdatePending; } void Togl_SwapBuffers(const Togl *togl) { if (togl->DoubleFlag) { #if defined(TOGL_WGL) int res = SwapBuffers(togl->tglGLHdc); if (!res) { ErrorExit(TEXT("SwapBuffers")); } #elif defined(TOGL_X11) glXSwapBuffers(Tk_Display(togl->TkWin), Tk_WindowId(togl->TkWin)); #elif defined(TOGL_AGL) aglSwapBuffers(togl->Ctx); #elif defined(TOGL_NSOPENGL) [togl->Ctx flushBuffer]; #endif } else { glFlush(); } } const char * Togl_Ident(const Togl *togl) { return togl->Ident; } int Togl_Width(const Togl *togl) { return togl->Width; } int Togl_Height(const Togl *togl) { return togl->Height; } int Togl_PixelScale(const Togl *togl) { return togl->PixelScale; } Tcl_Interp * Togl_Interp(const Togl *togl) { return togl->Interp; } Tk_Window Togl_TkWin(const Togl *togl) { return togl->TkWin; } const char * Togl_CommandName(const Togl *togl) { return Tcl_GetCommandName(togl->Interp, togl->widgetCmd); } int Togl_ContextTag(const Togl *togl) { return togl->contextTag; } Bool Togl_HasRGBA(const Togl *togl) { return togl->RgbaFlag; } Bool Togl_IsDoubleBuffered(const Togl *togl) { return togl->DoubleFlag; } Bool Togl_HasDepthBuffer(const Togl *togl) { return togl->DepthFlag; } Bool Togl_HasAccumulationBuffer(const Togl *togl) { return togl->AccumFlag; } Bool Togl_HasDestinationAlpha(const Togl *togl) { return togl->AlphaFlag; } Bool Togl_HasStencilBuffer(const Togl *togl) { return togl->StencilFlag; } int Togl_StereoMode(const Togl *togl) { return togl->Stereo; } Bool Togl_HasMultisample(const Togl *togl) { return togl->MultisampleFlag; } #if defined(TOGL_X11) /* * A replacement for XAllocColor. This function should never * fail to allocate a color. When XAllocColor fails, we return * the nearest matching color. If we have to allocate many colors * this function isn't too efficient; the XQueryColors() could be * done just once. * Written by Michael Pichler, Brian Paul, Mark Kilgard * Input: dpy - X display * cmap - X colormap * cmapSize - size of colormap * In/Out: color - the XColor struct * Output: exact - 1=exact color match, 0=closest match */ static void noFaultXAllocColor(Display *dpy, Colormap cmap, int cmapSize, XColor *color, int *exact) { XColor *ctable, subColor; int i, bestmatch; double mindist; /* 3*2^16^2 exceeds long int precision. */ /* First try just using XAllocColor. */ if (XAllocColor(dpy, cmap, color)) { *exact = 1; return; } /* Retrieve color table entries. */ /* XXX alloca candidate. */ ctable = (XColor *) ckalloc(cmapSize * sizeof (XColor)); for (i = 0; i < cmapSize; i++) { ctable[i].pixel = i; } (void) XQueryColors(dpy, cmap, ctable, cmapSize); /* Find best match. */ bestmatch = -1; mindist = 0; for (i = 0; i < cmapSize; i++) { double dr = (double) color->red - (double) ctable[i].red; double dg = (double) color->green - (double) ctable[i].green; double db = (double) color->blue - (double) ctable[i].blue; double dist = dr * dr + dg * dg + db * db; if (bestmatch < 0 || dist < mindist) { bestmatch = i; mindist = dist; } } /* Return result. */ subColor.red = ctable[bestmatch].red; subColor.green = ctable[bestmatch].green; subColor.blue = ctable[bestmatch].blue; ckfree((char *) ctable); /* Try to allocate the closest match color. This should only fail if the * cell is read/write. Otherwise, we're incrementing the cell's reference * count. */ if (!XAllocColor(dpy, cmap, &subColor)) { /* do this to work around a problem reported by Frank Ortega */ subColor.pixel = (unsigned long) bestmatch; subColor.red = ctable[bestmatch].red; subColor.green = ctable[bestmatch].green; subColor.blue = ctable[bestmatch].blue; subColor.flags = DoRed | DoGreen | DoBlue; } *color = subColor; } #elif defined(TOGL_WGL) static UINT Win32AllocColor(const Togl *togl, float red, float green, float blue) { /* Modified version of XAllocColor emulation of Tk. - returns index, * instead of color itself - allocates logical palette entry even for * non-palette devices */ TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin); UINT index; COLORREF newColor, closeColor; PALETTEENTRY entry, closeEntry; int isNew, refCount; Tcl_HashEntry *entryPtr; entry.peRed = (unsigned char) (red * 255 + .5); entry.peGreen = (unsigned char) (green * 255 + .5); entry.peBlue = (unsigned char) (blue * 255 + .5); entry.peFlags = 0; /* * Find the nearest existing palette entry. */ newColor = RGB(entry.peRed, entry.peGreen, entry.peBlue); index = GetNearestPaletteIndex(cmap->palette, newColor); GetPaletteEntries(cmap->palette, index, 1, &closeEntry); closeColor = RGB(closeEntry.peRed, closeEntry.peGreen, closeEntry.peBlue); /* * If this is not a duplicate and colormap is not full, allocate a new entry. */ if (newColor != closeColor) { if (cmap->size == (unsigned int) togl->CiColormapSize) { entry = closeEntry; } else { cmap->size++; ResizePalette(cmap->palette, cmap->size); index = cmap->size - 1; SetPaletteEntries(cmap->palette, index, 1, &entry); SelectPalette(togl->tglGLHdc, cmap->palette, TRUE); RealizePalette(togl->tglGLHdc); } } newColor = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue); entryPtr = Tcl_CreateHashEntry(&cmap->refCounts, (CONST char *) newColor, &isNew); if (isNew) { refCount = 1; } else { refCount = ((int) Tcl_GetHashValue(entryPtr)) + 1; } Tcl_SetHashValue(entryPtr, (ClientData) refCount); /* for color index mode photos */ togl->RedMap[index] = (GLfloat) (entry.peRed / 255.0); togl->GreenMap[index] = (GLfloat) (entry.peGreen / 255.0); togl->BlueMap[index] = (GLfloat) (entry.peBlue / 255.0); return index; } static void Win32FreeColor(const Togl *togl, unsigned long index) { TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin); COLORREF cref; UINT count, refCount; PALETTEENTRY entry, *entries; Tcl_HashEntry *entryPtr; if (index >= cmap->size) { panic("Tried to free a color that isn't allocated."); } GetPaletteEntries(cmap->palette, index, 1, &entry); cref = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue); entryPtr = Tcl_FindHashEntry(&cmap->refCounts, (CONST char *) cref); if (!entryPtr) { panic("Tried to free a color that isn't allocated."); } refCount = (int) Tcl_GetHashValue(entryPtr) - 1; if (refCount == 0) { count = cmap->size - index; entries = (PALETTEENTRY *) ckalloc(sizeof (PALETTEENTRY) * count); GetPaletteEntries(cmap->palette, index + 1, count, entries); SetPaletteEntries(cmap->palette, index, count, entries); SelectPalette(togl->tglGLHdc, cmap->palette, TRUE); RealizePalette(togl->tglGLHdc); ckfree((char *) entries); cmap->size--; Tcl_DeleteHashEntry(entryPtr); } else { Tcl_SetHashValue(entryPtr, (ClientData) refCount); } } static void Win32SetColor(const Togl *togl, unsigned long index, float red, float green, float blue) { TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin); PALETTEENTRY entry; entry.peRed = (unsigned char) (red * 255 + .5); entry.peGreen = (unsigned char) (green * 255 + .5); entry.peBlue = (unsigned char) (blue * 255 + .5); entry.peFlags = 0; SetPaletteEntries(cmap->palette, index, 1, &entry); SelectPalette(togl->tglGLHdc, cmap->palette, TRUE); RealizePalette(togl->tglGLHdc); /* for color index mode photos */ togl->RedMap[index] = (GLfloat) (entry.peRed / 255.0); togl->GreenMap[index] = (GLfloat) (entry.peGreen / 255.0); togl->BlueMap[index] = (GLfloat) (entry.peBlue / 255.0); } #endif /* TOGL_X11 */ unsigned long Togl_AllocColor(const Togl *togl, float red, float green, float blue) { if (togl->RgbaFlag) { (void) fprintf(stderr, "Error: Togl_AllocColor illegal in RGBA mode.\n"); return 0; } /* TODO: maybe not... */ if (togl->PrivateCmapFlag) { (void) fprintf(stderr, "Error: Togl_AllocColor illegal with private colormap\n"); return 0; } #if defined(TOGL_X11) { XColor xcol; int exact; xcol.red = (short) (red * 65535.0); xcol.green = (short) (green * 65535.0); xcol.blue = (short) (blue * 65535.0); noFaultXAllocColor(Tk_Display(togl->TkWin), Tk_Colormap(togl->TkWin), Tk_Visual(togl->TkWin)->map_entries, &xcol, &exact); /* for color index mode photos */ togl->RedMap[xcol.pixel] = (float) xcol.red / 65535.0; togl->GreenMap[xcol.pixel] = (float) xcol.green / 65535.0; togl->BlueMap[xcol.pixel] = (float) xcol.blue / 65535.0; return xcol.pixel; } #elif defined(TOGL_WGL) return Win32AllocColor(togl, red, green, blue); #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL) /* still need to implement this on Mac... */ return 0; #endif } void Togl_FreeColor(const Togl *togl, unsigned long pixel) { if (togl->RgbaFlag) { (void) fprintf(stderr, "Error: Togl_FreeColor illegal in RGBA mode.\n"); return; } /* TODO: maybe not... */ if (togl->PrivateCmapFlag) { (void) fprintf(stderr, "Error: Togl_FreeColor illegal with private colormap\n"); return; } #if defined(TOGL_X11) (void) XFreeColors(Tk_Display(togl->TkWin), Tk_Colormap(togl->TkWin), &pixel, 1, 0); #elif defined(TOGL_WGL) Win32FreeColor(togl, pixel); #endif } void Togl_SetColor(const Togl *togl, unsigned long index, float red, float green, float blue) { if (togl->RgbaFlag) { (void) fprintf(stderr, "Error: Togl_SetColor illegal in RGBA mode.\n"); return; } if (!togl->PrivateCmapFlag) { (void) fprintf(stderr, "Error: Togl_SetColor requires a private colormap\n"); return; } #if defined(TOGL_X11) { XColor xcol; xcol.pixel = index; xcol.red = (short) (red * 65535.0); xcol.green = (short) (green * 65535.0); xcol.blue = (short) (blue * 65535.0); xcol.flags = DoRed | DoGreen | DoBlue; (void) XStoreColor(Tk_Display(togl->TkWin), Tk_Colormap(togl->TkWin), &xcol); /* for color index mode photos */ togl->RedMap[xcol.pixel] = (float) xcol.red / 65535.0; togl->GreenMap[xcol.pixel] = (float) xcol.green / 65535.0; togl->BlueMap[xcol.pixel] = (float) xcol.blue / 65535.0; } #elif defined(TOGL_WGL) Win32SetColor(togl, index, red, green, blue); #endif } #if TOGL_USE_FONTS == 1 # include "toglFont.c" #else Tcl_Obj * Togl_LoadBitmapFont(const Togl *togl, const char *fontname) { return NULL; } int Togl_UnloadBitmapFont(const Togl *togl, Tcl_Obj *bitmapfont) { return TCL_OK; } int Togl_WriteObj(const Togl *togl, const Tcl_Obj *toglfont, Tcl_Obj *obj) { return -1; } int Togl_WriteChars(const Togl *togl, const Tcl_Obj *toglfont, const char *str, int len) { return -1; } #endif /* TOGL_USE_FONTS */ /* * Overlay functions */ void Togl_UseLayer(Togl *togl, int layer) { if (layer == TOGL_NORMAL) { #if defined(TOGL_WGL) int res = wglMakeCurrent(togl->tglGLHdc, togl->Ctx); if (!res) { ErrorExit(TEXT("wglMakeCurrent")); } #elif defined(TOGL_X11) (void) glXMakeCurrent(Tk_Display(togl->TkWin), Tk_WindowId(togl->TkWin), togl->Ctx); #elif defined(TOGL_AGL) (void) aglSetCurrentContext(togl->Ctx); #elif defined(TOGL_NSOPENGL) [togl->Ctx makeCurrentContext]; #endif } else if (layer == TOGL_OVERLAY && togl->OverlayWindow) { #if defined(TOGL_WGL) int res = wglMakeCurrent(togl->tglGLHdc, togl->tglGLOverlayHglrc); if (!res) { ErrorExit(TEXT("wglMakeCurrent overlay")); } #elif defined(TOGL_X11) (void) glXMakeCurrent(Tk_Display(togl->TkWin), togl->OverlayWindow, togl->OverlayCtx); #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL) #endif } else { /* error */ } } void Togl_ShowOverlay(Togl *togl) { #if defined(TOGL_X11) /* not yet implemented on Windows */ if (togl->OverlayWindow) { (void) XMapWindow(Tk_Display(togl->TkWin), togl->OverlayWindow); (void) XInstallColormap(Tk_Display(togl->TkWin), togl->OverlayCmap); togl->OverlayIsMapped = True; } #endif } void Togl_HideOverlay(Togl *togl) { if (togl->OverlayWindow && togl->OverlayIsMapped) { (void) XUnmapWindow(Tk_Display(togl->TkWin), togl->OverlayWindow); togl->OverlayIsMapped = False; } } void Togl_PostOverlayRedisplay(Togl *togl) { if (!togl->OverlayUpdatePending && togl->OverlayWindow && togl->OverlayDisplayProc) { Tk_DoWhenIdle(Togl_RenderOverlay, (ClientData) togl); togl->OverlayUpdatePending = True; } } int Togl_ExistsOverlay(const Togl *togl) { return togl->OverlayFlag; } int Togl_GetOverlayTransparentValue(const Togl *togl) { return togl->OverlayTransparentPixel; } int Togl_IsMappedOverlay(const Togl *togl) { return togl->OverlayFlag && togl->OverlayIsMapped; } unsigned long Togl_AllocColorOverlay(const Togl *togl, float red, float green, float blue) { #if defined(TOGL_X11) /* not yet implemented on Windows */ if (togl->OverlayFlag && togl->OverlayCmap) { XColor xcol; xcol.red = (short) (red * 65535.0); xcol.green = (short) (green * 65535.0); xcol.blue = (short) (blue * 65535.0); if (!XAllocColor(Tk_Display(togl->TkWin), togl->OverlayCmap, &xcol)) return (unsigned long) -1; return xcol.pixel; } #endif /* TOGL_X11 */ return (unsigned long) -1; } void Togl_FreeColorOverlay(const Togl *togl, unsigned long pixel) { #if defined(TOGL_X11) /* not yet implemented on Windows */ if (togl->OverlayFlag && togl->OverlayCmap) { (void) XFreeColors(Tk_Display(togl->TkWin), togl->OverlayCmap, &pixel, 1, 0); } #endif /* TOGL_X11 */ } /* * User client data */ ClientData Togl_GetClientData(const Togl *togl) { return togl->Client_Data; } void Togl_SetClientData(Togl *togl, ClientData clientData) { togl->Client_Data = clientData; } int Togl_CopyContext(const Togl *from, const Togl *to, unsigned mask) { #ifdef TOGL_X11 int error_code; int same = (glXGetCurrentContext() == to->Ctx); if (same) (void) glXMakeCurrent(to->display, None, NULL); togl_SetupXErrorHandler(); glXCopyContext(from->display, from->Ctx, to->Ctx, mask); if (error_code = togl_CheckForXError(from)) { char buf[256]; XGetErrorText(from->display, error_code, buf, sizeof buf); Tcl_AppendResult(from->Interp, "unable to copy context: ", buf, NULL); return TCL_ERROR; } #elif defined(TOGL_WGL) int same = (wglGetCurrentContext() == to->Ctx); if (same) (void) wglMakeCurrent(to->tglGLHdc, NULL); if (!wglCopyContext(from->Ctx, to->Ctx, mask)) { char buf[256]; snprintf(buf, sizeof buf, "unable to copy context: %d", GetLastError()); Tcl_AppendElement(from->Interp, buf); return TCL_ERROR; } #elif defined(TOGL_AGL) int same = (aglGetCurrentContext() == to->Ctx); if (same) (void) aglSetCurrentContext(NULL); if (!aglCopyContext(from->Ctx, to->Ctx, mask)) { Tcl_AppendResult(from->Interp, "unable to copy context: ", aglErrorString(aglGetError()), NULL); return TCL_ERROR; } #elif defined(TOGL_NSOPENGL) int same = (from->Ctx == to->Ctx); if (same) { [NSOpenGLContext clearCurrentContext]; } [to->Ctx copyAttributesFromContext:from->Ctx withMask:mask]; #endif if (same) Togl_MakeCurrent(to); return TCL_OK; } #ifdef MESA_COLOR_HACK /* * Let's know how many free colors do we have */ # define RLEVELS 5 # define GLEVELS 9 # define BLEVELS 5 /* to free dithered_rgb_colormap pixels allocated by Mesa */ static unsigned long *ToglMesaUsedPixelCells = NULL; static int ToglMesaUsedFreeCells = 0; static int get_free_color_cells(Display *display, int screen, Colormap colormap) { if (!ToglMesaUsedPixelCells) { XColor xcol; int i; int colorsfailed, ncolors = XDisplayCells(display, screen); long r, g, b; ToglMesaUsedPixelCells = (unsigned long *) ckalloc(ncolors * sizeof (unsigned long)); /* Allocate X colors and initialize color_table[], red_table[], etc */ /* de Mesa 2.1: xmesa1.c setup_dithered_(...) */ i = colorsfailed = 0; for (r = 0; r < RLEVELS; r++) for (g = 0; g < GLEVELS; g++) for (b = 0; b < BLEVELS; b++) { int exact; xcol.red = (r * 65535) / (RLEVELS - 1); xcol.green = (g * 65535) / (GLEVELS - 1); xcol.blue = (b * 65535) / (BLEVELS - 1); noFaultXAllocColor(display, colormap, ncolors, &xcol, &exact); ToglMesaUsedPixelCells[i++] = xcol.pixel; if (!exact) { colorsfailed++; } } ToglMesaUsedFreeCells = i; XFreeColors(display, colormap, ToglMesaUsedPixelCells, ToglMesaUsedFreeCells, 0x00000000); } return ToglMesaUsedFreeCells; } static void free_default_color_cells(Display *display, Colormap colormap) { if (ToglMesaUsedPixelCells) { XFreeColors(display, colormap, ToglMesaUsedPixelCells, ToglMesaUsedFreeCells, 0x00000000); ckfree((char *) ToglMesaUsedPixelCells); ToglMesaUsedPixelCells = NULL; ToglMesaUsedFreeCells = 0; } } #endif /* * Original stereo code contributed by Ben Evans (Ben.Evans@anusf.anu.edu.au) * and was based on SGI's /usr/share/src/OpenGL/teach/stereo/glwstereo.c, * which is identical to the 1997/12/1 glwstereo.c code in the CrystalEyes * Software Development Kit. */ int Togl_NumEyes(const Togl *togl) { if (togl->Stereo > TOGL_STEREO_ONE_EYE_MAX) return 2; return 1; } /* call instead of glDrawBuffer */ void Togl_DrawBuffer(Togl *togl, GLenum mode) { if (togl->Stereo <= TOGL_STEREO_ONE_EYE_MAX) { /* Only drawing a single eye */ if (togl->currentStereoBuffer != STEREO_BUFFER_NONE) { Togl_SetViewPort(togl); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); togl->currentStereoBuffer = STEREO_BUFFER_NONE; } switch (mode) { case GL_FRONT: case GL_BACK: case GL_FRONT_AND_BACK: break; case GL_LEFT: case GL_FRONT_LEFT: case GL_RIGHT: case GL_FRONT_RIGHT: mode = GL_FRONT; break; case GL_BACK_LEFT: case GL_BACK_RIGHT: mode = GL_BACK; break; default: break; } glDrawBuffer(mode); return; } /* called once for each eye */ switch (mode) { case GL_FRONT: case GL_BACK: case GL_FRONT_AND_BACK: /* ** Simultaneous drawing to both left and right buffers isn't ** really possible if we don't have a stereo capable visual. ** For now just fall through and use the left buffer. */ case GL_LEFT: case GL_FRONT_LEFT: case GL_BACK_LEFT: togl->currentStereoBuffer = STEREO_BUFFER_LEFT; break; case GL_RIGHT: case GL_FRONT_RIGHT: case GL_BACK_RIGHT: togl->currentStereoBuffer = STEREO_BUFFER_RIGHT; break; default: break; } if (togl->Stereo != TOGL_STEREO_NATIVE) { switch (mode) { default: mode = GL_FRONT; break; case GL_BACK: case GL_BACK_LEFT: case GL_BACK_RIGHT: mode = GL_BACK; break; } } int w = togl->Width*togl->PixelScale, h = togl->Height*togl->PixelScale; switch (togl->Stereo) { default: break; #ifdef __sgi case TOGL_STEREO_SGIOLDSTYLE: glXWaitGL(); /* sync with GL command stream before calling X */ XSGISetStereoBuffer(togl->display, Tk_WindowId(togl->TkWin), togl->currentStereoBuffer); glXWaitX(); /* sync with X command stream before calling GL */ break; #endif case TOGL_STEREO_ANAGLYPH: if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE); else glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE); glViewport(0, 0, w, h); break; case TOGL_STEREO_CROSS_EYE: if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) glViewport(w / 2 + 1, 0, w / 2, h); else glViewport(0, 0, w / 2, h); break; case TOGL_STEREO_WALL_EYE: if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) glViewport(0, 0, w / 2, h); else glViewport(w / 2 + 1, 0, w / 2, h); break; case TOGL_STEREO_DTI: if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) glViewport(0, 0, w / 2, h); else glViewport(w / 2 + 1, 0, w / 2, h); break; case TOGL_STEREO_ROW_INTERLEAVED: glViewport(0, 0, w, h); break; } glDrawBuffer(mode); } /* call instead of glClear */ void Togl_Clear(const Togl *togl, GLbitfield mask) { GLint stencil_write_mask = 0; GLint stencil_clear_value = 0; switch (togl->Stereo) { default: break; case TOGL_STEREO_CROSS_EYE: case TOGL_STEREO_WALL_EYE: case TOGL_STEREO_DTI: if (togl->currentStereoBuffer != STEREO_BUFFER_LEFT) { /* Since glViewport does not affect what is cleared (unlike * glScissor), only clear in left eye */ return; } break; case TOGL_STEREO_ROW_INTERLEAVED: if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) { if ((mask & GL_STENCIL_BUFFER_BIT) == 0) { mask |= GL_STENCIL_BUFFER_BIT; glStencilMask(~0u); glClearStencil(0); } else { glGetIntegerv(GL_STENCIL_WRITEMASK, &stencil_write_mask); glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencil_clear_value); glStencilMask(stencil_write_mask | togl->riStencilBit); glClearStencil(stencil_clear_value & ~togl->riStencilBit); } } else { mask &= ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } break; } #if 0 /* only needed if we wish to support multi-eye clears */ if (togl->Stereo > TOGL_STEREO_ONE_EYE_MAX) { GLenum drawBuffer = togl->currentDrawBuffer; switch (drawBuffer) { case GL_FRONT: Togl_DrawBuffer(togl, GL_FRONT_RIGHT); glClear(mask); Togl_DrawBuffer(togl, drawBuffer); break; case GL_BACK: Togl_DrawBuffer(togl, GL_BACK_RIGHT); glClear(mask); Togl_DrawBuffer(togl, drawBuffer); break; case GL_FRONT_AND_BACK: Togl_DrawBuffer(togl, GL_RIGHT); glClear(mask); Togl_DrawBuffer(togl, drawBuffer); break; case GL_LEFT: case GL_FRONT_LEFT: case GL_BACK_LEFT: case GL_RIGHT: case GL_FRONT_RIGHT: case GL_BACK_RIGHT: default: break; } } #endif if (mask != 0) glClear(mask); if (togl->Stereo == TOGL_STEREO_ROW_INTERLEAVED) { int x, y; if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) { int i; /* initialize stencil buffer mask */ glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_VIEWPORT_BIT); // 2d projection Togl_SetViewPort(togl); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, togl->Width, 0, togl->Height, -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTranslatef(0.375f, 0.375f, 0); glDisable(GL_ALPHA_TEST); glDisable(GL_COLOR_LOGIC_OP); glDisable(GL_DEPTH_TEST); glDisable(GL_DITHER); glDisable(GL_INDEX_LOGIC_OP); glDisable(GL_LIGHTING); glDisable(GL_LINE_SMOOTH); glDisable(GL_MULTISAMPLE); glLineWidth(1); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilFunc(GL_ALWAYS, togl->riStencilBit, togl->riStencilBit); glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); glBegin(GL_LINES); for (i = 0; i < togl->Height; i += 2) { glVertex2i(0, i); glVertex2i(togl->Width, i); } glEnd(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); if (stencil_write_mask) { glStencilMask(stencil_write_mask & ~togl->riStencilBit); } else { glStencilMask(~togl->riStencilBit); } Tk_GetRootCoords(togl->TkWin, &x, &y); if ((y + togl->Height) % 2) { glStencilFunc(GL_NOTEQUAL, togl->riStencilBit, togl->riStencilBit); } else { glStencilFunc(GL_EQUAL, togl->riStencilBit, togl->riStencilBit); } } else { Tk_GetRootCoords(togl->TkWin, &x, &y); if ((y + togl->Height) % 2) { glStencilFunc(GL_EQUAL, togl->riStencilBit, togl->riStencilBit); } else { glStencilFunc(GL_NOTEQUAL, togl->riStencilBit, togl->riStencilBit); } } } } /* * Togl_Frustum and Togl_Ortho: * * eyeOffset is the distance from the center line * and is negative for the left eye and positive for right eye. * eyeDist and eyeOffset need to be in the same units as your model space. * In physical space, eyeDist might be 30 inches from the screen * and eyeDist would be +/- 1.25 inch (for a total interocular distance * of 2.5 inches). */ void Togl_Frustum(const Togl *togl, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) { GLdouble eyeOffset = 0, eyeShift = 0; if (togl->Stereo == TOGL_STEREO_LEFT_EYE || togl->currentStereoBuffer == STEREO_BUFFER_LEFT) eyeOffset = -togl->EyeSeparation / 2; /* for left eye */ else if (togl->Stereo == TOGL_STEREO_RIGHT_EYE || togl->currentStereoBuffer == STEREO_BUFFER_RIGHT) eyeOffset = togl->EyeSeparation / 2; /* for right eye */ eyeShift = (togl->Convergence - zNear) * (eyeOffset / togl->Convergence); /* compensate for altered viewports */ switch (togl->Stereo) { default: break; case TOGL_STEREO_SGIOLDSTYLE: case TOGL_STEREO_DTI: /* squished image is expanded, nothing needed */ break; case TOGL_STEREO_CROSS_EYE: case TOGL_STEREO_WALL_EYE:{ GLdouble delta = (top - bottom) / 2; top += delta; bottom -= delta; break; } } glFrustum(left + eyeShift, right + eyeShift, bottom, top, zNear, zFar); glTranslated(-eyeShift, 0, 0); } void Togl_Ortho(const Togl *togl, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar) { /* TODO: debug this */ GLdouble eyeOffset = 0, eyeShift = 0; if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) eyeOffset = -togl->EyeSeparation / 2; /* for left eye */ else if (togl->currentStereoBuffer == STEREO_BUFFER_RIGHT) eyeOffset = togl->EyeSeparation / 2; /* for right eye */ eyeShift = (togl->Convergence - zNear) * (eyeOffset / togl->Convergence); /* compensate for altered viewports */ switch (togl->Stereo) { default: break; case TOGL_STEREO_SGIOLDSTYLE: case TOGL_STEREO_DTI: /* squished image is expanded, nothing needed */ break; case TOGL_STEREO_CROSS_EYE: case TOGL_STEREO_WALL_EYE:{ GLdouble delta = (top - bottom) / 2; top += delta; bottom -= delta; break; } } glOrtho(left + eyeShift, right + eyeShift, bottom, top, zNear, zFar); glTranslated(-eyeShift, 0, 0); } int Togl_GetToglFromObj(Tcl_Interp *interp, Tcl_Obj *obj, Togl **toglPtr) { Tcl_Command toglCmd; Tcl_CmdInfo info; toglCmd = Tcl_GetCommandFromObj(interp, obj); if (Tcl_GetCommandInfoFromToken(toglCmd, &info) == 0 || info.objProc != Togl_ObjWidget) { Tcl_AppendResult(interp, "expected togl command argument", NULL); return TCL_ERROR; } *toglPtr = (Togl *) info.objClientData; return TCL_OK; } int Togl_GetToglFromName(Tcl_Interp *interp, const char *cmdName, Togl **toglPtr) { Tcl_CmdInfo info; if (Tcl_GetCommandInfo(interp, cmdName, &info) == 0 || info.objProc != Togl_ObjWidget) { Tcl_AppendResult(interp, "expected togl command argument", NULL); return TCL_ERROR; } *toglPtr = (Togl *) info.objClientData; return TCL_OK; } static int ObjectIsEmpty(Tcl_Obj *objPtr); /* *---------------------------------------------------------------------- * * GetStereo - * * Converts an internal int into a Tcl string obj. * * Results: * Tcl_Obj containing the string representation of the stereo value. * * Side effects: * Creates a new Tcl_Obj. * *---------------------------------------------------------------------- */ static Tcl_Obj * GetStereo(ClientData clientData, Tk_Window tkwin, char *recordPtr, int internalOffset) /* recordPtr is a pointer to widget record. */ /* internalOffset is the offset within *recordPtr containing the stereo * value. */ { int stereo = *(int *) (recordPtr + internalOffset); const char *name = "unknown"; switch (stereo) { case TOGL_STEREO_NONE: name = ""; break; case TOGL_STEREO_LEFT_EYE: name = "left eye"; break; case TOGL_STEREO_RIGHT_EYE: name = "right eye"; break; case TOGL_STEREO_NATIVE: name = "native"; break; case TOGL_STEREO_SGIOLDSTYLE: name = "sgioldstyle"; break; case TOGL_STEREO_ANAGLYPH: name = "anaglyph"; break; case TOGL_STEREO_CROSS_EYE: name = "cross-eye"; break; case TOGL_STEREO_WALL_EYE: name = "wall-eye"; break; case TOGL_STEREO_DTI: name = "dti"; break; case TOGL_STEREO_ROW_INTERLEAVED: name = "row interleaved"; break; } return Tcl_NewStringObj(name, -1); } /* *---------------------------------------------------------------------- * * SetStereo -- * * Converts a Tcl_Obj representing a widgets stereo into an * integer value. * * Results: * Standard Tcl result. * * Side effects: * May store the integer value into the internal representation * pointer. May change the pointer to the Tcl_Obj to NULL to indicate * that the specified string was empty and that is acceptable. * *---------------------------------------------------------------------- */ static int SetStereo(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj **value, char *recordPtr, int internalOffset, char *oldInternalPtr, int flags) /* interp is the current interp; may be used for errors. */ /* tkwin is the Window for which option is being set. */ /* value is a pointer to the pointer to the value object. We use a pointer * to the pointer because we may need to return a value (NULL). */ /* recordPtr is a pointer to storage for the widget record. */ /* internalOffset is the offset within *recordPtr at which the internal * value is to be stored. */ /* oldInternalPtr is a pointer to storage for the old value. */ /* flags are the flags for the option, set Tk_SetOptions. */ { int stereo = 0; char *string, *internalPtr; internalPtr = (internalOffset > 0) ? recordPtr + internalOffset : NULL; if ((flags & TK_OPTION_NULL_OK) && ObjectIsEmpty(*value)) { *value = NULL; } else { /* * Convert the stereo specifier into an integer value. */ if (Tcl_GetBooleanFromObj(NULL, *value, &stereo) == TCL_OK) { stereo = stereo ? TOGL_STEREO_NATIVE : TOGL_STEREO_NONE; } else { string = Tcl_GetString(*value); if (strcmp(string, "") == 0 || strcasecmp(string, "none") == 0) { stereo = TOGL_STEREO_NONE; } else if (strcasecmp(string, "native") == 0) { stereo = TOGL_STEREO_NATIVE; /* check if available when creating visual */ } else if (strcasecmp(string, "left eye") == 0) { stereo = TOGL_STEREO_LEFT_EYE; } else if (strcasecmp(string, "right eye") == 0) { stereo = TOGL_STEREO_RIGHT_EYE; } else if (strcasecmp(string, "sgioldstyle") == 0) { stereo = TOGL_STEREO_SGIOLDSTYLE; } else if (strcasecmp(string, "anaglyph") == 0) { stereo = TOGL_STEREO_ANAGLYPH; } else if (strcasecmp(string, "cross-eye") == 0) { stereo = TOGL_STEREO_CROSS_EYE; } else if (strcasecmp(string, "wall-eye") == 0) { stereo = TOGL_STEREO_WALL_EYE; } else if (strcasecmp(string, "dti") == 0) { stereo = TOGL_STEREO_DTI; } else if (strcasecmp(string, "row interleaved") == 0) { stereo = TOGL_STEREO_ROW_INTERLEAVED; } else { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "bad stereo value \"", Tcl_GetString(*value), "\"", NULL); return TCL_ERROR; } } } if (internalPtr != NULL) { *((int *) oldInternalPtr) = *((int *) internalPtr); *((int *) internalPtr) = stereo; } return TCL_OK; } /* *---------------------------------------------------------------------- * RestoreStereo -- * * Restore a stereo option value from a saved value. * * Results: * None. * * Side effects: * Restores the old value. * *---------------------------------------------------------------------- */ static void RestoreStereo(ClientData clientData, Tk_Window tkwin, char *internalPtr, char *oldInternalPtr) { *(int *) internalPtr = *(int *) oldInternalPtr; } /* *---------------------------------------------------------------------- * * GetWideInt - * * Converts an internal wide integer into a Tcl WideInt obj. * * Results: * Tcl_Obj containing the wide int value. * * Side effects: * Creates a new Tcl_Obj. * *---------------------------------------------------------------------- */ static Tcl_Obj * GetWideInt(ClientData clientData, Tk_Window tkwin, char *recordPtr, int internalOffset) /* recordPtr is a pointer to widget record. */ /* internalOffset is the offset within *recordPtr containing the wide int * value. */ { Tcl_WideInt wi = *(Tcl_WideInt *) (recordPtr + internalOffset); return Tcl_NewWideIntObj(wi); } /* *---------------------------------------------------------------------- * * SetWideInt -- * * Converts a Tcl_Obj representing a Tcl_WideInt. * * Results: * Standard Tcl result. * * Side effects: * May store the wide int value into the internal representation * pointer. May change the pointer to the Tcl_Obj to NULL to indicate * that the specified string was empty and that is acceptable. * *---------------------------------------------------------------------- */ static int SetWideInt(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj **value, char *recordPtr, int internalOffset, char *oldInternalPtr, int flags) /* interp is the current interp; may be used for errors. */ /* tkwin is the Window for which option is being set. */ /* value is a pointer to the pointer to the value object. We use a pointer * to the pointer because we may need to return a value (NULL). */ /* recordPtr is a pointer to storage for the widget record. */ /* internalOffset is the offset within *recordPtr at which the internal * value is to be stored. */ /* oldInternalPtr is a pointer to storage for the old value. */ /* flags are the flags for the option, set Tk_SetOptions. */ { char *internalPtr; Tcl_WideInt w; internalPtr = (internalOffset > 0) ? recordPtr + internalOffset : NULL; if ((flags & TK_OPTION_NULL_OK) && ObjectIsEmpty(*value)) { *value = NULL; w = 0; } else { if (Tcl_GetWideIntFromObj(interp, *value, &w) != TCL_OK) { return TCL_ERROR; } } if (internalPtr != NULL) { *((Tcl_WideInt *) oldInternalPtr) = *((Tcl_WideInt *) internalPtr); *((Tcl_WideInt *) internalPtr) = w; } return TCL_OK; } /* *---------------------------------------------------------------------- * RestoreWideInt -- * * Restore a wide int option value from a saved value. * * Results: * None. * * Side effects: * Restores the old value. * *---------------------------------------------------------------------- */ static void RestoreWideInt(ClientData clientData, Tk_Window tkwin, char *internalPtr, char *oldInternalPtr) { *(Tcl_WideInt *) internalPtr = *(Tcl_WideInt *) oldInternalPtr; } /* *---------------------------------------------------------------------- * * ObjectIsEmpty -- * * This procedure tests whether the string value of an object is * empty. * * Results: * The return value is 1 if the string value of objPtr has length * zero, and 0 otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ObjectIsEmpty(Tcl_Obj *objPtr) /* objPtr = Object to test. May be NULL. */ { int length; if (objPtr == NULL) { return 1; } if (objPtr->bytes != NULL) { return (objPtr->length == 0); } Tcl_GetStringFromObj(objPtr, &length); return (length == 0); }