mirror of
https://github.com/NGSolve/netgen.git
synced 2024-12-25 21:40:33 +05:00
5418 lines
165 KiB
C
5418 lines
165 KiB
C
/* $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 <tkInt.h> // don't need it on osx ???
|
||
#include <limits.h>
|
||
|
||
#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 <windows.h>
|
||
# undef WIN32_LEAN_AND_MEAN
|
||
# include <winnt.h>
|
||
# 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 <GL/glext.h>
|
||
# include <objbase.h>
|
||
# include <GL/wglext.h>
|
||
# ifdef _MSC_VER
|
||
# include <strsafe.h>
|
||
# else
|
||
# ifdef UNICODE
|
||
# define StringCchPrintf snwprintf
|
||
# else
|
||
# define StringCchPrintf snprintf
|
||
# endif
|
||
# endif
|
||
|
||
/*** X Window System headers ***/
|
||
#elif defined(TOGL_X11)
|
||
# include <X11/Xlib.h>
|
||
# include <X11/Xutil.h>
|
||
# include <X11/Xatom.h> /* for XA_RGB_DEFAULT_MAP atom */
|
||
# if !defined(USE_SYSTEM_XMU)
|
||
# include "Xmu/StdCmap.h"
|
||
# else
|
||
# if defined(__vms)
|
||
# include <X11/StdCmap.h> /* for XmuLookupStandardColormap */
|
||
# else
|
||
# include <X11/Xmu/StdCmap.h> /* for XmuLookupStandardColormap */
|
||
# endif
|
||
# endif
|
||
# define GLX_GLXEXT_LEGACY /* include glxext.h separately */
|
||
# include <GL/glx.h>
|
||
/* 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 <GL/glxext.h>
|
||
# ifdef __sgi
|
||
# include <X11/extensions/SGIStereo.h>
|
||
# endif
|
||
# ifdef HAVE_AUTOSTEREO
|
||
# include <autostereo.h>
|
||
# endif
|
||
|
||
/*** Mac Carbon headers ***/
|
||
#elif defined(TOGL_AGL)
|
||
# define Cursor QDCursor
|
||
# include <AGL/agl.h>
|
||
# undef Cursor
|
||
# include <tkMacOSXInt.h> /* usa MacDrawable */
|
||
# include <ApplicationServices/ApplicationServices.h>
|
||
# define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr)
|
||
|
||
/*** Mac Cocoa headers ***/
|
||
#elif defined(TOGL_NSOPENGL)
|
||
#undef panic
|
||
# include <OpenGL/OpenGL.h>
|
||
# include <AppKit/NSOpenGL.h> /* Use NSOpenGLContext */
|
||
# include <AppKit/NSView.h> /* Use NSView */
|
||
# include <AppKit/NSWindow.h> /* Use NSWindow */
|
||
# include <AppKit/NSOpenGLView.h> /* Use NSView setWantsBestResolutionOpenGLSurface */
|
||
# include <AppKit/NSEvent.h> /* Use NSEvent */
|
||
# include <AppKit/NSTouch.h> /* Use NSTouch */
|
||
# include <Foundation/Foundation.h> /* Use NSRect */
|
||
# include <tkMacOSXInt.h> /* Use MacDrawable */
|
||
# include <ApplicationServices/ApplicationServices.h>
|
||
# define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr)
|
||
|
||
# include <Availability.h>
|
||
# 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 <X11/extensions/SGIStereo.h>, 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 <assert.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#ifdef TOGL_WGL
|
||
# include <tkPlatDecls.h>
|
||
#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 <tkWinInt.h>
|
||
# 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 <http://msdn2.microsoft.com/en-us/library/ms680582.aspx> */
|
||
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);
|
||
}
|