1 /* $Id: togl.c,v 1.142 2009/12/23 21:50:49 gregcouch Exp $ */
2 
3 /* vi:set sw=4 expandtab: */
4 
5 /*
6  * Togl - a Tk OpenGL widget
7  *
8  * Copyright (C) 1996-2002  Brian Paul and Ben Bederson
9  * Copyright (C) 2005-2009  Greg Couch
10  * See the LICENSE file for copyright details.
11  */
12 
13 /*
14  * Currently we support X11, Win32 and Mac OS X only
15  */
16 
17 #define USE_TOGL_STUB_PROCS
18 #include "togl.h"
19 #include <tkInt.h>   // don't need it on osx ???
20 #include <limits.h>
21 
22 #ifndef TOGL_USE_FONTS
23 #  define TOGL_USE_FONTS 1
24 #endif
25 #if (TK_MAJOR_VERSION > 8 || TK_MINOR_VERSION > 4) && !defined(TOGL_WGL)
26 /* X11 and Aqua font technology changed in 8.5 */
27 #  undef TOGL_USE_FONTS
28 #endif
29 #ifndef TOGL_USE_OVERLAY
30 #  if defined(TOGL_X11) || defined(TOGL_WGL)
31 #    define TOGL_USE_OVERLAY 1
32 #  endif
33 #endif
34 
35 /* Use TCL_STUPID to cast (const char *) to (char *) where the Tcl function
36  * prototype argument should really be const */
37 #define TCL_STUPID (char *)
38 
39 /* Use WIDGREC to cast widgRec or recordPtr arguments */
40 #define WIDGREC	(char *)
41 
42 /*** Windows headers ***/
43 #if defined(TOGL_WGL)
44 #  define WIN32_LEAN_AND_MEAN
45 #  include <windows.h>
46 #  undef WIN32_LEAN_AND_MEAN
47 #  include <winnt.h>
48 #  ifndef PFD_SUPPORT_COMPOSITION
49 // for Vista -- not strictly needed because we don't use PFD_SUPPORT_GDI/BITMAP
50 #    define PFD_SUPPORT_COMPOSITION 0x00008000
51 #  endif
52 #  include <GL/glext.h>
53 #  include <objbase.h>
54 #  include <GL/wglext.h>
55 #  ifdef _MSC_VER
56 #    include <strsafe.h>
57 #  else
58 #    ifdef UNICODE
59 #      define StringCchPrintf snwprintf
60 #    else
61 #      define StringCchPrintf snprintf
62 #    endif
63 #  endif
64 
65 /*** X Window System headers ***/
66 #elif defined(TOGL_X11)
67 #  include <X11/Xlib.h>
68 #  include <X11/Xutil.h>
69 #  include <X11/Xatom.h>        /* for XA_RGB_DEFAULT_MAP atom */
70 #  if !defined(USE_SYSTEM_XMU)
71 #    include "Xmu/StdCmap.h"
72 #  else
73 #    if defined(__vms)
74 #      include <X11/StdCmap.h>  /* for XmuLookupStandardColormap */
75 #    else
76 #      include <X11/Xmu/StdCmap.h>      /* for XmuLookupStandardColormap */
77 #    endif
78 #  endif
79 #  define GLX_GLXEXT_LEGACY     /* include glxext.h separately */
80 #  include <GL/glx.h>
81    /* we want the prototype typedefs from glxext.h */
82 #  undef GLX_VERSION_1_3
83 #  undef GLX_VERSION_1_4
84 #  ifdef UNDEF_GET_PROC_ADDRESS
85 #    undef GLX_ARB_get_proc_address
86 #  endif
87 #  include <GL/glxext.h>
88 #  ifdef __sgi
89 #    include <X11/extensions/SGIStereo.h>
90 #  endif
91 #  ifdef HAVE_AUTOSTEREO
92 #    include <autostereo.h>
93 #  endif
94 
95 /*** Mac Carbon headers ***/
96 #elif defined(TOGL_AGL)
97 #  define Cursor QDCursor
98 #  include <AGL/agl.h>
99 #  undef Cursor
100 #  include <tkMacOSXInt.h>      /* usa MacDrawable */
101 #  include <ApplicationServices/ApplicationServices.h>
102 #  define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr)
103 
104 /*** Mac Cocoa headers ***/
105 #elif defined(TOGL_NSOPENGL)
106 #undef panic
107 #  include <OpenGL/OpenGL.h>
108 #  include <AppKit/NSOpenGL.h>	/* Use NSOpenGLContext */
109 #  include <AppKit/NSView.h>	/* Use NSView */
110 #  include <AppKit/NSWindow.h>	/* Use NSWindow */
111 #  include <AppKit/NSOpenGLView.h>	/* Use NSView setWantsBestResolutionOpenGLSurface */
112 #  include <AppKit/NSEvent.h>	/* Use NSEvent */
113 #  include <AppKit/NSTouch.h>	/* Use NSTouch */
114 #  include <Foundation/Foundation.h>	/* Use NSRect */
115 #  include <tkMacOSXInt.h>      /* Use MacDrawable */
116 #  include <ApplicationServices/ApplicationServices.h>
117 #  define Togl_MacOSXGetDrawablePort(togl) TkMacOSXGetDrawablePort((Drawable) ((TkWindow *) togl->TkWin)->privatePtr)
118 
119 #  include <Availability.h>
120 #  ifndef __MAC_10_7
121 /* Define Mac retina display routines not available prior to Mac OS 10.7 */
122 @interface NSView (NSOpenGLSurfaceResolution)
123 - (BOOL)wantsBestResolutionOpenGLSurface;
124 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
125 - (NSRect)convertRectToBacking:(NSRect)aRect;
126 #define NSEventPhaseNone 0
127 @end
128 #  endif
129 
130 #else /* make sure only one platform defined */
131 #  error Unsupported platform, or confused platform defines...
132 #endif
133 
134 #define NC3D L"NVidia Consumer 3D Stereo"
135 
136 #ifndef STEREO_BUFFER_NONE
137 /* From <X11/extensions/SGIStereo.h>, but we use this constants elsewhere */
138 #  define STEREO_BUFFER_NONE 0
139 #  define STEREO_BUFFER_LEFT 1
140 #  define STEREO_BUFFER_RIGHT 2
141 #endif
142 
143 /*** Standard C headers ***/
144 #include <assert.h>
145 #include <stdlib.h>
146 #include <string.h>
147 
148 #ifdef TOGL_WGL
149 #  include <tkPlatDecls.h>
150 #endif
151 
152 #if TK_MAJOR_VERSION < 8
153 #  error Sorry Togl requires Tcl/Tk ver 8.0 or higher.
154 #endif
155 
156 #ifdef USE_TCL_STUBS
157 #  if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 1)
158 #    error Sorry stub support requires Tcl/Tk ver 8.1 or higher.
159 #  endif
160 #endif
161 
162 #if defined(TOGL_AGL)
163 #  if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 4)
164 #    error Sorry Mac Aqua version requires Tcl/Tk ver 8.4.0 or higher.
165 #  endif
166 #endif /* TOGL_AGL */
167 
168 // Seems to work with Apple Tcl 8.5 too....
169 // #if defined(TOGL_NSOPENGL)
170 // #  if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 6)
171 // #    error Sorry Mac Cocoa version requires Tcl/Tk ver 8.6.0 or higher.
172 // #  endif
173 // #endif /* TOGL_NSOPENGL */
174 
175 #if defined(TOGL_WGL) && defined(_MSC_VER)
176 #  define snprintf _snprintf
177 #  pragma warning(disable:4995)
178 #endif
179 
180 /* workaround for bug #123153 in tcl ver8.4a2 (tcl.h) */
181 #if defined(Tcl_InitHashTable) && defined(USE_TCL_STUBS)
182 #  undef Tcl_InitHashTable
183 #  define Tcl_InitHashTable (tclStubsPtr->tcl_InitHashTable)
184 #endif
185 #if TK_MAJOR_VERSION > 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION >= 4)
186 #  define HAVE_TK_SETCLASSPROCS
187 /* pointer to Tk_SetClassProcs function in the stub table */
188 
189 #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 6
190 static void (*SetClassProcsPtr)
191         _ANSI_ARGS_((Tk_Window, Tk_ClassProcs *, ClientData));
192 #else
193 static void (*SetClassProcsPtr)
194         _ANSI_ARGS_((Tk_Window, const Tk_ClassProcs *, ClientData));
195 #endif
196 #endif
197 
198 /*
199  * Copy of TkClassProcs declarations from tkInt.h
200  * (this is needed for Tcl ver =< 8.4a3)
201  */
202 
203 typedef Window (TkClassCreateProc) _ANSI_ARGS_((Tk_Window tkwin,
204                 Window parent, ClientData instanceData));
205 typedef void (TkClassGeometryProc) _ANSI_ARGS_((ClientData instanceData));
206 typedef void (TkClassModalProc) _ANSI_ARGS_((Tk_Window tkwin,
207                 XEvent *eventPtr));
208 typedef struct TkClassProcs
209 {
210     TkClassCreateProc *createProc;
211     TkClassGeometryProc *geometryProc;
212     TkClassModalProc *modalProc;
213 } TkClassProcs;
214 
215 
216 /* Defaults */
217 #define DEFAULT_WIDTH		"400"
218 #define DEFAULT_HEIGHT		"400"
219 #define DEFAULT_IDENT		""
220 #define DEFAULT_FONTNAME	"Courier"
221 #define DEFAULT_TIME		"1"
222 
223 
224 #ifdef TOGL_WGL
225 /* Maximum size of a logical palette corresponding to a colormap in color index
226  * mode. */
227 #  define MAX_CI_COLORMAP_SIZE 4096
228 #  define MAX_CI_COLORMAP_BITS 12
229 
230 #  if TOGL_USE_FONTS != 1
231 /*
232  * copy of TkWinColormap from tkWinInt.h
233  */
234 
235 typedef struct
236 {
237     HPALETTE palette;           /* Palette handle used when drawing. */
238     UINT    size;               /* Number of entries in the palette. */
239     int     stale;              /* 1 if palette needs to be realized, otherwise
240                                  * 0.  If the palette is stale, then an idle
241                                  * handler is scheduled to realize the palette.
242                                  */
243     Tcl_HashTable refCounts;    /* Hash table of palette entry reference counts
244                                  * indexed by pixel value. */
245 } TkWinColormap;
246 #  else
247 #    include <tkWinInt.h>
248 #  endif
249 
250 static  LRESULT(CALLBACK *tkWinChildProc) (HWND hwnd, UINT message,
251         WPARAM wParam, LPARAM lParam) = NULL;
252 
253 #  ifndef TK_WIN_CHILD_CLASS_NAME
254 #    define TK_WIN_CHILD_CLASS_NAME L"TkChild"
255 #  endif
256 
257 #endif /* TOGL_WGL */
258 
259 
260 #define MAX(a,b)	(((a)>(b))?(a):(b))
261 
262 #define TCL_ERR(interp, string)			\
263    do {						\
264       Tcl_ResetResult(interp);			\
265       Tcl_AppendResult(interp, string, NULL);	\
266       return TCL_ERROR;				\
267    } while (0)
268 
269 #define ALL_EVENTS_MASK 	\
270    (KeyPressMask		\
271    |KeyReleaseMask		\
272    |ButtonPressMask		\
273    |ButtonReleaseMask		\
274    |EnterWindowMask		\
275    |LeaveWindowMask		\
276    |PointerMotionMask		\
277    |ExposureMask		\
278    |VisibilityChangeMask	\
279    |FocusChangeMask		\
280    |PropertyChangeMask		\
281    |ColormapChangeMask)
282 
283 /*
284  * The following structure contains pointers to functions used for
285  * processing the custom "-stereo" option.  Copied from tkPanedWindow.c.
286  */
287 static int SetStereo(ClientData clientData, Tcl_Interp *interp,
288         Tk_Window tkwin, Tcl_Obj **value, char *recordPtr,
289         int internalOffset, char *oldInternalPtr, int flags);
290 static Tcl_Obj *GetStereo(ClientData clientData, Tk_Window tkwin,
291         char *recordPtr, int internalOffset);
292 static void RestoreStereo(ClientData clientData, Tk_Window tkwin,
293         char *internalPtr, char *oldInternalPtr);
294 
295 static Tk_ObjCustomOption stereoOption = {
296     "stereo",                   /* name */
297     SetStereo,                  /* setProc */
298     GetStereo,                  /* getProc */
299     RestoreStereo,              /* restoreProc */
300     NULL,                       /* freeProc */
301     0
302 };
303 
304 /*
305  * The following structure contains pointers to functions used for
306  * processing the custom "-pixelformat" option.  Copied from tkPanedWindow.c.
307  */
308 static int SetWideInt(ClientData clientData, Tcl_Interp *interp,
309         Tk_Window tkwin, Tcl_Obj **value, char *recordPtr,
310         int internalOffset, char *oldInternalPtr, int flags);
311 static Tcl_Obj *GetWideInt(ClientData clientData, Tk_Window tkwin,
312         char *recordPtr, int internalOffset);
313 static void RestoreWideInt(ClientData clientData, Tk_Window tkwin,
314         char *internalPtr, char *oldInternalPtr);
315 
316 static Tk_ObjCustomOption wideIntOption = {
317     "wide int",                 /* name */
318     SetWideInt,                 /* setProc */
319     GetWideInt,                 /* getProc */
320     RestoreWideInt,             /* restoreProc */
321     NULL,                       /* freeProc */
322     0
323 };
324 
325 /*
326  * Stuff we initialize on a per package (Togl_Init) basis.
327  * Since Tcl uses one interpreter per thread, any per-thread
328  * data goes here.
329  */
330 struct Togl_PackageGlobals
331 {
332     Tk_OptionTable optionTable; /* Used to parse options */
333     Togl   *toglHead;           /* Head of linked list of all Togl widgets */
334     int     nextContextTag;     /* Used to assign similar context tags */
335 };
336 typedef struct Togl_PackageGlobals Togl_PackageGlobals;
337 
338 extern ToglStubs toglStubs;     /* should be only non-const global */
339 
340 #if defined(TOGL_NSOPENGL)
341 
342 /* Handle window drags between retina and non-retina displays. */
343 @interface ToglNSView : NSView {
344     struct Togl *togl;
345 }
346 - (void) setTogl: (struct Togl*)t;
347 - (void) viewDidChangeBackingProperties;
348 - (void) magnifyWithEvent: (NSEvent *)event;
349 - (void) rotateWithEvent: (NSEvent *)event;
350 - (void) scrollWheel: (NSEvent *)event;
351 - (void)touchesBeganWithEvent:(NSEvent *)event;
352 - (void)touchesMovedWithEvent:(NSEvent *)event;
353 - (void)touchesEndedWithEvent:(NSEvent *)event;
354 - (void)touchesCancelledWithEvent:(NSEvent *)event;
355 - (void)reportEventTouches:(NSEvent *)event;
356 
357 @end
358 
359 #endif
360 
361 struct Togl
362 {
363     Togl   *Next;               /* next in linked list */
364 
365 #if defined(TOGL_WGL)
366     HGLRC   Ctx;                /* OpenGL rendering context to be made current */
367     HDC     tglGLHdc;           /* Device context of device that OpenGL calls
368                                  * will be drawn on */
369     int     CiColormapSize;     /* (Maximum) size of colormap in color index
370                                  * mode */
371 #elif defined(TOGL_X11)
372     GLXContext Ctx;             /* Normal planes GLX context */
373 #elif defined(TOGL_AGL)
374     AGLContext Ctx;
375 #elif defined(TOGL_NSOPENGL)
376     NSOpenGLContext *Ctx;
377     ToglNSView *nsview;
378 #endif
379     int     contextTag;         /* all contexts with same tag share display
380                                  * lists */
381 
382     XVisualInfo *VisInfo;       /* Visual info of the current */
383 
384     Display *display;           /* X's token for the window's display. */
385     Tk_Window TkWin;            /* Tk window structure */
386     Tcl_Interp *Interp;         /* Tcl interpreter */
387     Tcl_Command widgetCmd;      /* Token for togl's widget command */
388     Togl_PackageGlobals *tpg;   /* Used to access globals */
389 #ifndef NO_TK_CURSOR
390     Tk_Cursor Cursor;           /* The widget's cursor */
391 #endif
392     int     Width, Height;      /* Dimensions of window */
393     int     PixelScale;         /* Graphics pixels per Tk pixel. */
394                                 /*    1 for normal display. */
395                                 /*    2 for Mac retina display. */
396     int     SetGrid;            /* positive is grid size for window manager */
397     int     TimerInterval;      /* Time interval for timer in milliseconds */
398     Tcl_TimerToken timerHandler;        /* Token for togl's timer handler */
399     Bool    RgbaFlag;           /* configuration flags (ala GLX parameters) */
400     int     RgbaRed;
401     int     RgbaGreen;
402     int     RgbaBlue;
403     Bool    DoubleFlag;
404     Bool    DepthFlag;
405     int     DepthSize;
406     Bool    AccumFlag;
407     int     AccumRed;
408     int     AccumGreen;
409     int     AccumBlue;
410     int     AccumAlpha;
411     Bool    AlphaFlag;
412     int     AlphaSize;
413     Bool    StencilFlag;
414     int     StencilSize;
415     Bool    PrivateCmapFlag;
416     Bool    OverlayFlag;
417     int     Stereo;
418     double  EyeSeparation;
419     double  Convergence;
420     GLuint  riStencilBit;       /* row interleaved stencil bit */
421     int     AuxNumber;
422     Bool    Indirect;
423 #if defined(TOGL_NSOPENGL)
424     NSOpenGLPixelFormat *PixelFormat;
425 #else
426     Tcl_WideInt PixelFormat;
427 #endif
428     int     SwapInterval;
429     Bool    MultisampleFlag;
430     Bool    FullscreenFlag;
431     Bool    PbufferFlag;
432     Bool    LargestPbufferFlag;
433 #if defined(TOGL_X11)
434     GLXFBConfig fbcfg;          /* cache FBConfig for pbuffer creation */
435     GLXPbuffer pbuf;
436 #elif defined(TOGL_WGL)
437     HPBUFFERARB pbuf;
438     int     pbufferLost;
439 #elif defined(TOGL_AGL)
440     AGLPbuffer pbuf;
441 #elif defined(TOGL_NSOPENGL)
442     NSOpenGLPixelBuffer *pbuf;
443 #endif
444     const char *ShareList;      /* name (ident) of Togl to share dlists with */
445     const char *ShareContext;   /* name (ident) to share OpenGL context with */
446 
447     const char *Ident;          /* User's identification string */
448     ClientData Client_Data;     /* Pointer to user data */
449 
450     Bool    UpdatePending;      /* Should normal planes be redrawn? */
451 
452     Tcl_Obj *CreateProc;        /* Callback when widget is realized */
453     Tcl_Obj *DisplayProc;       /* Callback when widget is redrawn */
454     Tcl_Obj *ReshapeProc;       /* Callback when window size changes */
455     Tcl_Obj *DestroyProc;       /* Callback when widget is destroyed */
456     Tcl_Obj *TimerProc;         /* Callback when widget is idle */
457     Tcl_Obj *MagnifyProc;       /* Callback for track pad pinch gesture */
458     Tcl_Obj *RotateProc;        /* Callback for track pad rotate gesture */
459     Tcl_Obj *ScrollProc;        /* Callback for track pad scroll gesture */
460     Tcl_Obj *ScrollWheelProc;   /* Callback for mouse scroll wheel, not trackpad */
461     Tcl_Obj *TouchesProc;       /* Callback for track pad touches */
462 
463     /* Overlay stuff */
464 #if defined(TOGL_X11)
465     GLXContext OverlayCtx;      /* Overlay planes OpenGL context */
466 #elif defined(TOGL_WGL)
467     HGLRC   tglGLOverlayHglrc;
468 #endif
469 
470     Window  OverlayWindow;      /* The overlay window, or 0 */
471     Tcl_Obj *OverlayDisplayProc;        /* Overlay redraw proc */
472     Bool    OverlayUpdatePending;       /* Should overlay be redrawn? */
473     Colormap OverlayCmap;       /* colormap for overlay is created */
474     int     OverlayTransparentPixel;    /* transparent pixel */
475     Bool    OverlayIsMapped;
476 
477     GLfloat *RedMap;            /* Index2RGB Maps for Color index modes */
478     GLfloat *GreenMap;
479     GLfloat *BlueMap;
480     GLint   MapSize;            /* = Number of indices in our Togl */
481     int     currentStereoBuffer;
482 #ifdef HAVE_AUTOSTEREO
483     int     as_initialized;     /* for autostereo package */
484     ASHandle ash;               /* for autostereo package */
485 #endif
486     int     badWindow;          /* true when Togl_MakeWindow fails or should
487                                  * create a dummy window */
488 };
489 
490 int Togl_CallCallback(Togl *togl, Tcl_Obj *cmd);
491 static int Togl_CallCallback_P(Togl *togl, Tcl_Obj *cmd, double *params, int nparams);
492 
493 #if defined(TOGL_NSOPENGL)
494 
495 static int viewPixelScale(NSView *nsview);
496 @implementation ToglNSView
497 
498 - (void) setTogl: (struct Togl*)t
499 {
500     togl = t;
501 }
502 
503 - (void) viewDidChangeBackingProperties
504 {
505     [super viewDidChangeBackingProperties];
506     if (togl && togl->ReshapeProc) {
507       int pscale = viewPixelScale(self);
508       if (pscale != togl->PixelScale) {
509 	togl->PixelScale = pscale;
510 	if (togl->ReshapeProc)
511 	  (void) Togl_CallCallback(togl, togl->ReshapeProc);
512 	Togl_PostRedisplay(togl);
513       }
514     }
515 }
516 
517 - (void) magnifyWithEvent: (NSEvent *)event
518 {
519   if (togl && togl->MagnifyProc) {
520     double mag = [event magnification] + 1.0;
521     (void) Togl_CallCallback_P(togl, togl->MagnifyProc, &mag, 1);
522   }
523 }
524 
525 - (void) rotateWithEvent: (NSEvent *)event
526 {
527   if (togl && togl->RotateProc) {
528     double deg = [event rotation];
529     (void) Togl_CallCallback_P(togl, togl->RotateProc, &deg, 1);
530   }
531 }
532 
533 - (void) scrollWheel: (NSEvent *)event
534 {
535   if (togl && (togl->ScrollProc || togl->ScrollWheelProc)) {
536     float dx = [event deltaX], dy = [event deltaY];
537     if (dx != 0 || dy != 0) {
538       //      float track_pad = ((([event phase] == NSEventPhaseNone) &&
539       //			  ([event momentumPhase] == NSEventPhaseNone)) ? 0 : 1);
540       float track_pad = ([event subtype] == NSMouseEventSubtype ? 0 : 1);
541       double args[3] = {dx, dy, track_pad};
542       if (togl->ScrollProc)
543 	(void) Togl_CallCallback_P(togl, togl->ScrollProc, args, 3);
544       if (togl->ScrollWheelProc && !track_pad)
545 	(void) Togl_CallCallback_P(togl, togl->ScrollWheelProc, args+1, 1);
546     }
547   }
548 }
549 
550 - (void)touchesBeganWithEvent:(NSEvent *)event
551 {  NSWindow *win = [self window];
552   [win makeKeyWindow];
553   [win makeMainWindow];
554   [self reportEventTouches:event];
555  }
556 - (void)touchesMovedWithEvent:(NSEvent *)event
557 { [self reportEventTouches:event]; }
558 - (void)touchesEndedWithEvent:(NSEvent *)event
559 { [self reportEventTouches:event]; }
560 - (void)touchesCancelledWithEvent:(NSEvent *)event;
561 { [self reportEventTouches:event]; }
562 - (void)reportEventTouches: (NSEvent *)event
563 {
564   if (togl && togl->TouchesProc) {
565     NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseTouching inView:self];
566     int n = touches.count, i;
567     double *id_xy = NULL;
568     if (n >= 0) {
569       id_xy = (double *)malloc(3*n*sizeof(double));
570       NSArray *array = [touches allObjects];
571       for (i = 0 ; i < n ; ++i) {
572 	NSTouch *t = [array objectAtIndex:i];
573 	id_xy[3*i] = (long)t.identity;
574 	id_xy[3*i+1] = t.normalizedPosition.x * t.deviceSize.width;
575 	id_xy[3*i+2] = t.normalizedPosition.y * t.deviceSize.height;
576       }
577       (void) Togl_CallCallback_P(togl, togl->TouchesProc, id_xy, 3*n);
578       if (id_xy)
579 	free(id_xy);
580     }
581   }
582 }
583 
584 @end
585 
586 #endif
587 
588 /*
589  * Prototypes for functions local to this file
590  */
591 static int Togl_ObjCmd(ClientData clientData, Tcl_Interp *interp,
592         int objc, Tcl_Obj *const *objv);
593 static void Togl_ObjCmdDelete(ClientData clientData);
594 static void Togl_EventProc(ClientData clientData, XEvent *eventPtr);
595 static void Togl_RedisplayProc(ClientData clientData, XEvent *eventPtr);
596 static Window Togl_MakeWindow(Tk_Window, Window, ClientData);
597 static void Togl_WorldChanged(ClientData);
598 static void Togl_SetViewPort(const struct Togl *);
599 
600 #ifdef MESA_COLOR_HACK
601 static int get_free_color_cells(Display *display, int screen,
602         Colormap colormap);
603 static void free_default_color_cells(Display *display, Colormap colormap);
604 #endif
605 static void ToglCmdDeletedProc(ClientData);
606 
607 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
608 static void SetMacBufRect(Togl *togl);
609 #endif
610 
611 #if defined(TOGL_WGL)
612 #  include "toglWGL.c"
613 #elif defined(TOGL_AGL)
614 #  include "toglAGL.c"
615 #elif defined(TOGL_NSOPENGL)
616 #  include "toglNSOpenGL.c"
617 #elif defined(TOGL_X11)
618 #  include "toglGLX.c"
619 #endif
620 
621 
622 /*
623  * Setup Togl widget configuration options:
624  */
625 
626 #define GEOMETRY_MASK 0x1       /* widget geometry */
627 #define FORMAT_MASK 0x2         /* pixel format */
628 #define CURSOR_MASK 0x4
629 #define TIMER_MASK 0x8
630 #define OVERLAY_MASK 0x10
631 #define SWAP_MASK 0x20
632 #define STEREO_MASK 0x40
633 #define STEREO_FORMAT_MASK 0x80
634 
635 static Tk_OptionSpec optionSpecs[] = {
636     {TK_OPTION_PIXELS, TCL_STUPID "-height", "height", "Height",
637                 DEFAULT_HEIGHT, -1, Tk_Offset(Togl, Height), 0, NULL,
638             GEOMETRY_MASK},
639     {TK_OPTION_PIXELS, TCL_STUPID "-width", "width", "Width",
640                 DEFAULT_WIDTH, -1, Tk_Offset(Togl, Width), 0, NULL,
641             GEOMETRY_MASK},
642     {TK_OPTION_INT, TCL_STUPID "-pixelscale", "pixelscale", "PixelScale",
643             "1", -1, Tk_Offset(Togl, PixelScale), 0, NULL, FORMAT_MASK},
644     {TK_OPTION_BOOLEAN, TCL_STUPID "-rgba", "rgba", "Rgba",
645             "true", -1, Tk_Offset(Togl, RgbaFlag), 0, NULL, FORMAT_MASK},
646     {TK_OPTION_INT, TCL_STUPID "-redsize", "redsize", "RedSize",
647             "1", -1, Tk_Offset(Togl, RgbaRed), 0, NULL, FORMAT_MASK},
648     {TK_OPTION_INT, TCL_STUPID "-greensize", "greensize", "GreenSize",
649             "1", -1, Tk_Offset(Togl, RgbaGreen), 0, NULL, FORMAT_MASK},
650     {TK_OPTION_INT, TCL_STUPID "-bluesize", "bluesize", "BlueSize",
651             "1", -1, Tk_Offset(Togl, RgbaBlue), 0, NULL, FORMAT_MASK},
652     {TK_OPTION_BOOLEAN, TCL_STUPID "-double", "double", "Double",
653             "false", -1, Tk_Offset(Togl, DoubleFlag), 0, NULL, FORMAT_MASK},
654     {TK_OPTION_BOOLEAN, TCL_STUPID "-depth", "depth", "Depth",
655             "false", -1, Tk_Offset(Togl, DepthFlag), 0, NULL, FORMAT_MASK},
656     {TK_OPTION_INT, TCL_STUPID "-depthsize", "depthsize", "DepthSize",
657             "1", -1, Tk_Offset(Togl, DepthSize), 0, NULL, FORMAT_MASK},
658     {TK_OPTION_BOOLEAN, TCL_STUPID "-accum", "accum", "Accum",
659             "false", -1, Tk_Offset(Togl, AccumFlag), 0, NULL, FORMAT_MASK},
660     {TK_OPTION_INT, TCL_STUPID "-accumredsize", "accumredsize", "AccumRedSize",
661             "1", -1, Tk_Offset(Togl, AccumRed), 0, NULL, FORMAT_MASK},
662     {TK_OPTION_INT, TCL_STUPID "-accumgreensize", "accumgreensize",
663                 "AccumGreenSize",
664             "1", -1, Tk_Offset(Togl, AccumGreen), 0, NULL, FORMAT_MASK},
665     {TK_OPTION_INT, TCL_STUPID "-accumbluesize", "accumbluesize",
666                 "AccumBlueSize",
667             "1", -1, Tk_Offset(Togl, AccumBlue), 0, NULL, FORMAT_MASK},
668     {TK_OPTION_INT, TCL_STUPID "-accumalphasize", "accumalphasize",
669                 "AccumAlphaSize",
670             "1", -1, Tk_Offset(Togl, AccumAlpha), 0, NULL, FORMAT_MASK},
671     {TK_OPTION_BOOLEAN, TCL_STUPID "-alpha", "alpha", "Alpha",
672             "false", -1, Tk_Offset(Togl, AlphaFlag), 0, NULL, FORMAT_MASK},
673     {TK_OPTION_INT, TCL_STUPID "-alphasize", "alphasize", "AlphaSize",
674             "1", -1, Tk_Offset(Togl, AlphaSize), 0, NULL, FORMAT_MASK},
675     {TK_OPTION_BOOLEAN, TCL_STUPID "-stencil", "stencil", "Stencil",
676             "false", -1, Tk_Offset(Togl, StencilFlag), 0, NULL, FORMAT_MASK},
677     {TK_OPTION_INT, TCL_STUPID "-stencilsize", "stencilsize", "StencilSize",
678             "1", -1, Tk_Offset(Togl, StencilSize), 0, NULL, FORMAT_MASK},
679     {TK_OPTION_INT, TCL_STUPID "-auxbuffers", "auxbuffers", "AuxBuffers",
680             "0", -1, Tk_Offset(Togl, AuxNumber), 0, NULL, FORMAT_MASK},
681     {TK_OPTION_BOOLEAN, TCL_STUPID "-privatecmap", "privateCmap", "PrivateCmap",
682                 "false", -1, Tk_Offset(Togl, PrivateCmapFlag), 0, NULL,
683             FORMAT_MASK},
684     {TK_OPTION_BOOLEAN, TCL_STUPID "-overlay", "overlay", "Overlay",
685             "false", -1, Tk_Offset(Togl, OverlayFlag), 0, NULL, OVERLAY_MASK},
686     {TK_OPTION_CUSTOM, TCL_STUPID "-stereo", "stereo", "Stereo",
687                 "", -1, Tk_Offset(Togl, Stereo), 0,
688             (ClientData) &stereoOption, STEREO_FORMAT_MASK},
689     {TK_OPTION_DOUBLE, TCL_STUPID "-eyeseparation", "eyeseparation",
690                 "EyeSeparation",
691             "2.0", -1, Tk_Offset(Togl, EyeSeparation), 0, NULL, STEREO_MASK},
692     {TK_OPTION_DOUBLE, TCL_STUPID "-convergence", "convergence", "Convergence",
693             "35.0", -1, Tk_Offset(Togl, Convergence), 0, NULL, STEREO_MASK},
694 #ifndef NO_TK_CURSOR
695     {TK_OPTION_CURSOR, TCL_STUPID "-cursor", "cursor", "Cursor",
696                 "", -1, Tk_Offset(Togl, Cursor), TK_OPTION_NULL_OK, NULL,
697             CURSOR_MASK},
698 #endif
699     {TK_OPTION_INT, TCL_STUPID "-setgrid", "setGrid", "SetGrid",
700             "0", -1, Tk_Offset(Togl, SetGrid), 0, NULL, GEOMETRY_MASK},
701     {TK_OPTION_INT, TCL_STUPID "-time", "time", "Time",
702                 DEFAULT_TIME, -1, Tk_Offset(Togl, TimerInterval), 0, NULL,
703             TIMER_MASK},
704     {TK_OPTION_STRING, TCL_STUPID "-sharelist", "sharelist", "ShareList",
705             NULL, -1, Tk_Offset(Togl, ShareList), 0, NULL, FORMAT_MASK},
706     {TK_OPTION_STRING, TCL_STUPID "-sharecontext", "sharecontext",
707                 "ShareContext", NULL,
708             -1, Tk_Offset(Togl, ShareContext), 0, NULL, FORMAT_MASK},
709     {TK_OPTION_STRING, TCL_STUPID "-ident", "ident", "Ident",
710             DEFAULT_IDENT, -1, Tk_Offset(Togl, Ident), 0, NULL, 0},
711     {TK_OPTION_BOOLEAN, TCL_STUPID "-indirect", "indirect", "Indirect",
712             "false", -1, Tk_Offset(Togl, Indirect), 0, NULL, FORMAT_MASK},
713     {TK_OPTION_CUSTOM, TCL_STUPID "-pixelformat", "pixelFormat", "PixelFormat",
714                 "0", -1, Tk_Offset(Togl, PixelFormat), 0,
715             (ClientData) &wideIntOption, FORMAT_MASK},
716     {TK_OPTION_INT, TCL_STUPID "-swapinterval", "swapInterval", "SwapInterval",
717             "1", -1, Tk_Offset(Togl, SwapInterval), 0, NULL, SWAP_MASK},
718 #if 0
719     {TK_OPTION_BOOLEAN, TCL_STUPID "-fullscreen", "fullscreen", "Fullscreen",
720                 "false", -1, Tk_Offset(Togl, FullscreenFlag), 0, NULL,
721             GEOMETRY_MASK|FORMAT_MASK},
722 #endif
723     {TK_OPTION_BOOLEAN, TCL_STUPID "-multisample", "multisample", "Multisample",
724                 "false", -1, Tk_Offset(Togl, MultisampleFlag), 0, NULL,
725             FORMAT_MASK},
726     {TK_OPTION_BOOLEAN, TCL_STUPID "-pbuffer", "pbuffer", "Pbuffer",
727             "false", -1, Tk_Offset(Togl, PbufferFlag), 0, NULL, FORMAT_MASK},
728     {TK_OPTION_BOOLEAN, TCL_STUPID "-largestpbuffer", "largestpbuffer",
729                 "LargestPbuffer",
730             "false", -1, Tk_Offset(Togl, LargestPbufferFlag), 0, NULL, 0},
731     {TK_OPTION_STRING, TCL_STUPID "-createcommand", "createCommand",
732                 "CallbackCommand", NULL,
733             Tk_Offset(Togl, CreateProc), -1, TK_OPTION_NULL_OK, NULL, 0},
734     {TK_OPTION_SYNONYM, TCL_STUPID "-create", NULL, NULL,
735             NULL, -1, -1, 0, (ClientData) "-createcommand", 0},
736     {TK_OPTION_STRING, TCL_STUPID "-displaycommand", "displayCommand",
737                 "CallbackCommand", NULL,
738             Tk_Offset(Togl, DisplayProc), -1, TK_OPTION_NULL_OK, NULL, 0},
739     {TK_OPTION_SYNONYM, TCL_STUPID "-display", NULL, NULL,
740             NULL, -1, -1, 0, (ClientData) "-displaycommand", 0},
741     {TK_OPTION_STRING, TCL_STUPID "-reshapecommand", "reshapeCommand",
742                 "CallbackCommand", NULL,
743             Tk_Offset(Togl, ReshapeProc), -1, TK_OPTION_NULL_OK, NULL, 0},
744     {TK_OPTION_SYNONYM, TCL_STUPID "-reshape", NULL, NULL,
745             NULL, -1, -1, 0, (ClientData) "-reshapecommand", 0},
746     {TK_OPTION_STRING, TCL_STUPID "-destroycommand", "destroyCommand",
747                 "CallbackCommand", NULL,
748             Tk_Offset(Togl, DestroyProc), -1, TK_OPTION_NULL_OK, NULL, 0},
749     {TK_OPTION_SYNONYM, TCL_STUPID "-destroy", NULL, NULL,
750             NULL, -1, -1, 0, (ClientData) "-destroycommand", 0},
751     {TK_OPTION_STRING, TCL_STUPID "-timercommand", "timerCommand",
752                 "CallabckCommand", NULL,
753             Tk_Offset(Togl, TimerProc), -1, TK_OPTION_NULL_OK, NULL, 0},
754     {TK_OPTION_SYNONYM, TCL_STUPID "-timer", NULL, NULL,
755             NULL, -1, -1, 0, (ClientData) "-timercommand", 0},
756     {TK_OPTION_STRING, TCL_STUPID "-overlaydisplaycommand",
757                 "overlaydisplayCommand", "CallbackCommand", NULL,
758                 Tk_Offset(Togl, OverlayDisplayProc), -1,
759             TK_OPTION_NULL_OK, NULL, OVERLAY_MASK},
760     {TK_OPTION_SYNONYM, TCL_STUPID "-overlaydisplay", NULL, NULL,
761             NULL, -1, -1, 0, (ClientData) "-overlaydisplaycommand", 0},
762     {TK_OPTION_STRING, TCL_STUPID "-magnifycommand", "magnifyCommand",
763                 "CallbackCommand", NULL,
764             Tk_Offset(Togl, MagnifyProc), -1, TK_OPTION_NULL_OK, NULL, 0},
765     {TK_OPTION_STRING, TCL_STUPID "-rotatecommand", "rotateCommand",
766                 "CallbackCommand", NULL,
767             Tk_Offset(Togl, RotateProc), -1, TK_OPTION_NULL_OK, NULL, 0},
768     {TK_OPTION_STRING, TCL_STUPID "-scrollcommand", "scrollCommand",
769                 "CallbackCommand", NULL,
770             Tk_Offset(Togl, ScrollProc), -1, TK_OPTION_NULL_OK, NULL, 0},
771     {TK_OPTION_STRING, TCL_STUPID "-scrollwheelcommand", "scrollWheelCommand",
772                 "CallbackCommand", NULL,
773             Tk_Offset(Togl, ScrollWheelProc), -1, TK_OPTION_NULL_OK, NULL, 0},
774     {TK_OPTION_STRING, TCL_STUPID "-touchescommand", "touchesCommand",
775                 "CallbackCommand", NULL,
776             Tk_Offset(Togl, TouchesProc), -1, TK_OPTION_NULL_OK, NULL, 0},
777 
778     /* Tcl3D backwards compatibility */
779     {TK_OPTION_SYNONYM, TCL_STUPID "-createproc", NULL, NULL,
780             NULL, -1, -1, 0, (ClientData) "-createcommand", 0},
781     {TK_OPTION_SYNONYM, TCL_STUPID "-displayproc", NULL, NULL,
782             NULL, -1, -1, 0, (ClientData) "-displaycommand", 0},
783     {TK_OPTION_SYNONYM, TCL_STUPID "-reshapeproc", NULL, NULL,
784             NULL, -1, -1, 0, (ClientData) "-reshapecommand", 0},
785     /* end Tcl3D compatibility */
786     {TK_OPTION_END, NULL, NULL, NULL, NULL, -1, -1, 0, NULL, 0}
787 };
788 
789 /*
790  * Add given togl widget to linked list.
791  */
792 static void
AddToList(Togl * t)793 AddToList(Togl *t)
794 {
795     t->Next = t->tpg->toglHead;
796     t->tpg->toglHead = t;
797 }
798 
799 /*
800  * Remove given togl widget from linked list.
801  */
802 static void
RemoveFromList(Togl * t)803 RemoveFromList(Togl *t)
804 {
805     Togl   *prev;
806     Togl   *cur;
807 
808     for (cur = t->tpg->toglHead, prev = NULL; cur; prev = cur, cur = cur->Next) {
809         if (t != cur)
810             continue;
811         if (prev) {
812             prev->Next = cur->Next;
813         } else {
814             t->tpg->toglHead = cur->Next;
815         }
816         break;
817     }
818     if (cur)
819         cur->Next = NULL;
820 }
821 
822 /*
823  * Return pointer to togl widget given a user identifier string.
824  */
825 static Togl *
FindTogl(Togl * togl,const char * ident)826 FindTogl(Togl *togl, const char *ident)
827 {
828     Togl   *t;
829 
830     if (ident[0] != '.') {
831         for (t = togl->tpg->toglHead; t; t = t->Next) {
832             if (strcmp(t->Ident, ident) == 0)
833                 break;
834         }
835     } else {
836         for (t = togl->tpg->toglHead; t; t = t->Next) {
837             const char *pathname = Tk_PathName(t->TkWin);
838 
839             if (strcmp(pathname, ident) == 0)
840                 break;
841         }
842     }
843     return t;
844 }
845 
846 
847 /*
848  * Return pointer to another togl widget with same OpenGL context.
849  */
850 static Togl *
FindToglWithSameContext(const Togl * togl)851 FindToglWithSameContext(const Togl *togl)
852 {
853     Togl   *t;
854 
855     for (t = togl->tpg->toglHead; t != NULL; t = t->Next) {
856         if (t == togl)
857             continue;
858         if (t->Ctx == togl->Ctx) {
859             return t;
860         }
861     }
862     return NULL;
863 }
864 
865 #if TOGL_USE_OVERLAY
866 /*
867  * Return pointer to another togl widget with same OpenGL overlay context.
868  */
869 static Togl *
FindToglWithSameOverlayContext(const Togl * togl)870 FindToglWithSameOverlayContext(const Togl *togl)
871 {
872     Togl   *t;
873 
874     for (t = togl->tpg->toglHead; t != NULL; t = t->Next) {
875         if (t == togl)
876             continue;
877 #  if defined(TOGL_X11)
878         if (t->OverlayCtx == togl->OverlayCtx)
879 #  elif defined(TOGL_WGL)
880         if (t->tglGLOverlayHglrc == togl->tglGLOverlayHglrc)
881 #  endif
882         {
883             return t;
884         }
885     }
886     return NULL;
887 }
888 #endif
889 
890 #if defined(TOGL_X11)
891 /*
892  * Return an X colormap to use for OpenGL RGB-mode rendering.
893  * Input:  dpy - the X display
894  *         scrnum - the X screen number
895  *         visinfo - the XVisualInfo as returned by glXChooseVisual()
896  * Return:  an X Colormap or 0 if there's a _serious_ error.
897  */
898 static Colormap
get_rgb_colormap(Display * dpy,int scrnum,const XVisualInfo * visinfo,Tk_Window tkwin)899 get_rgb_colormap(Display *dpy,
900         int scrnum, const XVisualInfo *visinfo, Tk_Window tkwin)
901 {
902     Atom    hp_cr_maps;
903     Status  status;
904     int     numCmaps;
905     int     i;
906     XStandardColormap *standardCmaps;
907     Window  root = XRootWindow(dpy, scrnum);
908     Bool    using_mesa;
909 
910     /*
911      * First check if visinfo's visual matches the default/root visual.
912      */
913     if (visinfo->visual == Tk_Visual(tkwin)) {
914         /* use the default/root colormap */
915         Colormap cmap;
916 
917         cmap = Tk_Colormap(tkwin);
918 #  ifdef MESA_COLOR_HACK
919         (void) get_free_color_cells(dpy, scrnum, cmap);
920 #  endif
921         return cmap;
922     }
923 
924     /*
925      * Check if we're using Mesa.
926      */
927     if (strstr(glXQueryServerString(dpy, scrnum, GLX_VERSION), "Mesa")) {
928         using_mesa = True;
929     } else {
930         using_mesa = False;
931     }
932 
933     /*
934      * Next, if we're using Mesa and displaying on an HP with the "Color
935      * Recovery" feature and the visual is 8-bit TrueColor, search for a
936      * special colormap initialized for dithering.  Mesa will know how to
937      * dither using this colormap.
938      */
939     if (using_mesa) {
940         hp_cr_maps = XInternAtom(dpy, "_HP_RGB_SMOOTH_MAP_LIST", True);
941         if (hp_cr_maps
942 #  ifdef __cplusplus
943                 && visinfo->visual->c_class == TrueColor
944 #  else
945                 && visinfo->visual->class == TrueColor
946 #  endif
947                 && visinfo->depth == 8) {
948             status = XGetRGBColormaps(dpy, root, &standardCmaps,
949                     &numCmaps, hp_cr_maps);
950             if (status) {
951                 for (i = 0; i < numCmaps; i++) {
952                     if (standardCmaps[i].visualid == visinfo->visual->visualid) {
953                         Colormap cmap = standardCmaps[i].colormap;
954 
955                         (void) XFree(standardCmaps);
956                         return cmap;
957                     }
958                 }
959                 (void) XFree(standardCmaps);
960             }
961         }
962     }
963 
964     /*
965      * Next, try to find a standard X colormap.
966      */
967 #  if !HP && !SUN
968 #    ifndef SOLARIS_BUG
969     status = XmuLookupStandardColormap(dpy, visinfo->screen,
970             visinfo->visualid, visinfo->depth, XA_RGB_DEFAULT_MAP,
971             /* replace */ False, /* retain */ True);
972     if (status == 1) {
973         status = XGetRGBColormaps(dpy, root, &standardCmaps,
974                 &numCmaps, XA_RGB_DEFAULT_MAP);
975         if (status == 1) {
976             for (i = 0; i < numCmaps; i++) {
977                 if (standardCmaps[i].visualid == visinfo->visualid) {
978                     Colormap cmap = standardCmaps[i].colormap;
979 
980                     (void) XFree(standardCmaps);
981                     return cmap;
982                 }
983             }
984             (void) XFree(standardCmaps);
985         }
986     }
987 #    endif
988 #  endif
989 
990     /*
991      * If we get here, give up and just allocate a new colormap.
992      */
993     return XCreateColormap(dpy, root, visinfo->visual, AllocNone);
994 }
995 #elif defined(TOGL_WGL)
996 
997 /* Code to create RGB palette is taken from the GENGL sample program of Win32
998  * SDK */
999 
1000 static const unsigned char threeto8[8] = {
1001     0, 0111 >> 1, 0222 >> 1, 0333 >> 1, 0444 >> 1, 0555 >> 1, 0666 >> 1, 0377
1002 };
1003 
1004 static const unsigned char twoto8[4] = {
1005     0, 0x55, 0xaa, 0xff
1006 };
1007 
1008 static const unsigned char oneto8[2] = {
1009     0, 255
1010 };
1011 
1012 static const int defaultOverride[13] = {
1013     0, 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91
1014 };
1015 
1016 static const PALETTEENTRY defaultPalEntry[20] = {
1017     {0, 0, 0, 0},
1018     {0x80, 0, 0, 0},
1019     {0, 0x80, 0, 0},
1020     {0x80, 0x80, 0, 0},
1021     {0, 0, 0x80, 0},
1022     {0x80, 0, 0x80, 0},
1023     {0, 0x80, 0x80, 0},
1024     {0xC0, 0xC0, 0xC0, 0},
1025 
1026     {192, 220, 192, 0},
1027     {166, 202, 240, 0},
1028     {255, 251, 240, 0},
1029     {160, 160, 164, 0},
1030 
1031     {0x80, 0x80, 0x80, 0},
1032     {0xFF, 0, 0, 0},
1033     {0, 0xFF, 0, 0},
1034     {0xFF, 0xFF, 0, 0},
1035     {0, 0, 0xFF, 0},
1036     {0xFF, 0, 0xFF, 0},
1037     {0, 0xFF, 0xFF, 0},
1038     {0xFF, 0xFF, 0xFF, 0}
1039 };
1040 
1041 static unsigned char
ComponentFromIndex(int i,UINT nbits,UINT shift)1042 ComponentFromIndex(int i, UINT nbits, UINT shift)
1043 {
1044     unsigned char val;
1045 
1046     val = (unsigned char) (i >> shift);
1047     switch (nbits) {
1048 
1049       case 1:
1050           val &= 0x1;
1051           return oneto8[val];
1052 
1053       case 2:
1054           val &= 0x3;
1055           return twoto8[val];
1056 
1057       case 3:
1058           val &= 0x7;
1059           return threeto8[val];
1060 
1061       default:
1062           return 0;
1063     }
1064 }
1065 
1066 static Colormap
Win32CreateRgbColormap(PIXELFORMATDESCRIPTOR pfd)1067 Win32CreateRgbColormap(PIXELFORMATDESCRIPTOR pfd)
1068 {
1069     TkWinColormap *cmap = (TkWinColormap *) ckalloc(sizeof (TkWinColormap));
1070     LOGPALETTE *pPal;
1071     int     n, i;
1072 
1073     n = 1 << pfd.cColorBits;
1074     pPal = (PLOGPALETTE) LocalAlloc(LMEM_FIXED, sizeof (LOGPALETTE)
1075             + n * sizeof (PALETTEENTRY));
1076     pPal->palVersion = 0x300;
1077     pPal->palNumEntries = n;
1078     for (i = 0; i < n; i++) {
1079         pPal->palPalEntry[i].peRed =
1080                 ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift);
1081         pPal->palPalEntry[i].peGreen =
1082                 ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift);
1083         pPal->palPalEntry[i].peBlue =
1084                 ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift);
1085         pPal->palPalEntry[i].peFlags = 0;
1086     }
1087 
1088     /* fix up the palette to include the default GDI palette */
1089     if ((pfd.cColorBits == 8)
1090             && (pfd.cRedBits == 3) && (pfd.cRedShift == 0)
1091             && (pfd.cGreenBits == 3) && (pfd.cGreenShift == 3)
1092             && (pfd.cBlueBits == 2) && (pfd.cBlueShift == 6)) {
1093         for (i = 1; i <= 12; i++)
1094             pPal->palPalEntry[defaultOverride[i]] = defaultPalEntry[i];
1095     }
1096 
1097     cmap->palette = CreatePalette(pPal);
1098     LocalFree(pPal);
1099     cmap->size = n;
1100     cmap->stale = 0;
1101 
1102     /* Since this is a private colormap of a fix size, we do not need a valid
1103      * hash table, but a dummy one */
1104 
1105     Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS);
1106     return (Colormap) cmap;
1107 }
1108 
1109 static Colormap
Win32CreateCiColormap(Togl * togl)1110 Win32CreateCiColormap(Togl *togl)
1111 {
1112     /* Create a colormap with size of togl->CiColormapSize and set all entries
1113      * to black */
1114 
1115     LOGPALETTE logPalette;
1116     TkWinColormap *cmap = (TkWinColormap *) ckalloc(sizeof (TkWinColormap));
1117 
1118     logPalette.palVersion = 0x300;
1119     logPalette.palNumEntries = 1;
1120     logPalette.palPalEntry[0].peRed = 0;
1121     logPalette.palPalEntry[0].peGreen = 0;
1122     logPalette.palPalEntry[0].peBlue = 0;
1123     logPalette.palPalEntry[0].peFlags = 0;
1124 
1125     cmap->palette = CreatePalette(&logPalette);
1126     cmap->size = togl->CiColormapSize;
1127     ResizePalette(cmap->palette, cmap->size);   /* sets new entries to black */
1128     cmap->stale = 0;
1129 
1130     /* Since this is a private colormap of a fix size, we do not need a valid
1131      * hash table, but a dummy one */
1132 
1133     Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS);
1134     return (Colormap) cmap;
1135 }
1136 
1137 /* ErrorExit is from <http://msdn2.microsoft.com/en-us/library/ms680582.aspx> */
1138 static void
ErrorExit(LPTSTR lpszFunction)1139 ErrorExit(LPTSTR lpszFunction)
1140 {
1141     /* Retrieve the system error message for the last-error code */
1142     LPTSTR  lpMsgBuf;
1143     LPTSTR  lpDisplayBuf;
1144     DWORD   err = GetLastError();
1145 
1146     if (err == 0) {
1147         /* The function said it failed, but GetLastError says it didn't, so
1148          * pretend it didn't. */
1149         return;
1150     }
1151 
1152     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
1153             | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
1154             NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1155             (LPTSTR) &lpMsgBuf, 0, NULL);
1156 
1157     /* Display the error message and exit the process */
1158 
1159     lpDisplayBuf = (LPTSTR) LocalAlloc(LMEM_ZEROINIT,
1160             (lstrlen(lpMsgBuf) + lstrlen(lpszFunction) + 40) * sizeof (TCHAR));
1161     StringCchPrintf(lpDisplayBuf, LocalSize(lpDisplayBuf),
1162             TEXT("%s failed with error %ld: %s"), lpszFunction, err, lpMsgBuf);
1163     MessageBox(NULL, lpDisplayBuf, TEXT("Error"), MB_OK);
1164 
1165     LocalFree(lpMsgBuf);
1166     LocalFree(lpDisplayBuf);
1167     ExitProcess(err);
1168 }
1169 #endif
1170 
1171 /*
1172  * Togl_Init
1173  *
1174  *   Called upon system startup to create togl command.
1175  */
1176 int
Togl_Init(Tcl_Interp * interp)1177 Togl_Init(Tcl_Interp *interp)
1178 {
1179     int     major, minor, patchLevel, releaseType;
1180 
1181 #ifdef USE_TCL_STUBS
1182     if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
1183         return TCL_ERROR;
1184     }
1185 #endif
1186 #ifdef USE_TK_STUBS
1187     if (Tk_InitStubs(interp, TCL_STUPID "8.1", 0) == NULL) {
1188         return TCL_ERROR;
1189     }
1190 #endif
1191 
1192     Tcl_GetVersion(&major, &minor, &patchLevel, &releaseType);
1193 #ifdef HAVE_TK_SETCLASSPROCS
1194     if (major > 8
1195             || (major == 8
1196                     && (minor > 4
1197                             || (minor == 4 && (releaseType > 0
1198                                             || patchLevel >= 2))))) {
1199 #  ifdef USE_TK_STUBS
1200         SetClassProcsPtr = tkStubsPtr->tk_SetClassProcs;
1201 #  else
1202         SetClassProcsPtr = Tk_SetClassProcs;
1203 #  endif
1204     } else {
1205         SetClassProcsPtr = NULL;
1206     }
1207 #else
1208     if (major > 8
1209             || (major == 8
1210                     && (minor > 4
1211                             || (minor == 4 && (releaseType > 0
1212                                             || patchLevel >= 2))))) {
1213         TCL_ERR(interp,
1214                 "Sorry, this instance of Togl was not compiled to work with Tcl/Tk 8.4a2 or higher.");
1215     }
1216 #endif
1217 
1218     if (Tcl_CreateObjCommand(interp, "togl", Togl_ObjCmd, NULL,
1219                     Togl_ObjCmdDelete) == NULL) {
1220         return TCL_ERROR;
1221     }
1222 
1223     if (Tcl_PkgProvideEx(interp, "Togl", TOGL_VERSION, &toglStubs) != TCL_OK) {
1224         return TCL_ERROR;
1225     }
1226 
1227     return TCL_OK;
1228 }
1229 
1230 
1231 /*
1232  * Togl_CallCallback
1233  *
1234  * Call command with togl widget as only argument
1235  */
1236 
1237 int
Togl_CallCallback(Togl * togl,Tcl_Obj * cmd)1238 Togl_CallCallback(Togl *togl, Tcl_Obj *cmd)
1239 {
1240     int     result;
1241     Tcl_Obj *objv[3];
1242 
1243     if (cmd == NULL || togl->widgetCmd == NULL)
1244         return TCL_OK;
1245 
1246     objv[0] = cmd;
1247     Tcl_IncrRefCount(objv[0]);
1248     objv[1] =
1249             Tcl_NewStringObj(Tcl_GetCommandName(togl->Interp, togl->widgetCmd),
1250             -1);
1251     Tcl_IncrRefCount(objv[1]);
1252     objv[2] = NULL;
1253     result = Tcl_EvalObjv(togl->Interp, 2, objv, TCL_EVAL_GLOBAL);
1254     Tcl_DecrRefCount(objv[1]);
1255     Tcl_DecrRefCount(objv[0]);
1256     if (result != TCL_OK)
1257         Tcl_BackgroundError(togl->Interp);
1258     return result;
1259 }
1260 
1261 static int
Togl_CallCallback_P(Togl * togl,Tcl_Obj * cmd,double * params,int nparams)1262 Togl_CallCallback_P(Togl *togl, Tcl_Obj *cmd, double *params, int nparams)
1263 {
1264     int     result, i;
1265     Tcl_Obj **objv = (Tcl_Obj **)malloc((3+nparams)*sizeof(Tcl_Obj *));
1266 
1267     if (cmd == NULL || togl->widgetCmd == NULL)
1268         return TCL_OK;
1269 
1270     objv[0] = cmd;
1271     Tcl_IncrRefCount(objv[0]);
1272     objv[1] = Tcl_NewStringObj(Tcl_GetCommandName(togl->Interp, togl->widgetCmd), -1);
1273     Tcl_IncrRefCount(objv[1]);
1274     for (i = 0 ; i < nparams ; ++i) {
1275       objv[2+i] = Tcl_NewDoubleObj(params[i]);
1276       Tcl_IncrRefCount(objv[2+i]);
1277     }
1278     objv[2+nparams] = NULL;
1279     result = Tcl_EvalObjv(togl->Interp, 2+nparams, objv, TCL_EVAL_GLOBAL);
1280     for (i = 1+nparams ; i >= 0 ; --i)
1281       Tcl_DecrRefCount(objv[i]);
1282     free(objv);
1283     if (result != TCL_OK)
1284         Tcl_BackgroundError(togl->Interp);
1285     return result;
1286 }
1287 
1288 
1289 /*
1290  * Togl_Timer
1291  *
1292  * Gets called from Tk_CreateTimerHandler.
1293  */
1294 static void
Togl_Timer(ClientData clientData)1295 Togl_Timer(ClientData clientData)
1296 {
1297     Togl   *togl = (Togl *) clientData;
1298 
1299     if (togl->TimerProc) {
1300         if (Togl_CallCallback(togl, togl->TimerProc) != TCL_OK) {
1301             togl->timerHandler = NULL;
1302             return;
1303         }
1304         /*
1305          * Re-register this callback since Tcl/Tk timers are "one-shot".
1306          * That is, after the timer callback is called it not normally
1307          * called again.  That's not the behavior we want for Togl.
1308          */
1309         togl->timerHandler =
1310                 Tcl_CreateTimerHandler(togl->TimerInterval, Togl_Timer,
1311                 (ClientData) togl);
1312     }
1313 }
1314 
1315 
1316 /*
1317  * Togl_MakeCurrent
1318  *
1319  *   Bind the OpenGL rendering context to the specified
1320  *   Togl widget.  If given a NULL argument, then the
1321  *   OpenGL context is released without assigning a new one.
1322  */
1323 void
Togl_MakeCurrent(const Togl * togl)1324 Togl_MakeCurrent(const Togl *togl)
1325 {
1326 #if defined(TOGL_WGL)
1327     int     res = TRUE;
1328 
1329     if (togl == NULL) {
1330         HDC     hdc = wglGetCurrentDC();
1331 
1332         if (hdc != NULL)
1333             res = wglMakeCurrent(hdc, NULL);
1334     } else {
1335         if (togl->pbufferLost) {
1336             Bool    keepContext = FindToglWithSameContext(togl) != NULL;
1337             Togl   *t = (Togl *) togl;  /* conceptually const */
1338 
1339             if (!keepContext) {
1340                 wglDeleteContext(t->Ctx);
1341             }
1342             togl_destroyPbuffer(t);
1343             t->pbuf = togl_createPbuffer(t);
1344             if (!keepContext) {
1345                 t->Ctx = wglCreateContext(t->tglGLHdc);
1346             }
1347         }
1348         res = wglMakeCurrent(togl->tglGLHdc, togl->Ctx);
1349     }
1350     if (!res) {
1351         ErrorExit(TEXT("wglMakeCurrent"));
1352     }
1353 #elif defined(TOGL_X11)
1354     Display *display = togl ? togl->display : glXGetCurrentDisplay();
1355 
1356     if (display) {
1357         GLXDrawable drawable;
1358 
1359         if (!togl)
1360             drawable = None;
1361         else if (togl->PbufferFlag)
1362             drawable = togl->pbuf;
1363         else if (togl->TkWin)
1364             drawable = Tk_WindowId(togl->TkWin);
1365         else
1366             drawable = None;
1367         (void) glXMakeCurrent(display, drawable, drawable ? togl->Ctx : NULL);
1368     }
1369 #elif defined(TOGL_AGL)
1370     if (togl == NULL || togl->Ctx == NULL) {
1371         (void) aglSetCurrentContext(NULL);
1372     } else {
1373         (void) aglSetCurrentContext(togl->Ctx);
1374         if (FindToglWithSameContext(togl) != NULL) {
1375             if (!togl->PbufferFlag) {
1376                 AGLDrawable d = Togl_MacOSXGetDrawablePort(togl);
1377 
1378                 aglSetDrawable(togl->Ctx, d);
1379             } else {
1380                 GLint   virtualScreen = aglGetVirtualScreen(togl->Ctx);
1381 
1382                 aglSetPBuffer(togl->Ctx, togl->pbuf, 0, 0, virtualScreen);
1383             }
1384         }
1385     }
1386 #elif defined(TOGL_NSOPENGL)
1387     if (togl != NULL && togl->Ctx != NULL) {
1388         [togl->Ctx makeCurrentContext];
1389         if (FindToglWithSameContext(togl) != NULL) {
1390             if (!togl->PbufferFlag) {
1391 	        [togl->Ctx setView:togl->nsview];
1392             } else {
1393 	        GLint   virtualScreen =	[togl->Ctx currentVirtualScreen];
1394                 [togl->Ctx setPixelBuffer:togl->pbuf cubeMapFace:0
1395 		 mipMapLevel:0 currentVirtualScreen:virtualScreen];
1396             }
1397         }
1398     }
1399 #endif
1400 }
1401 
1402 /*
1403  * Togl_TakePhoto
1404  *
1405  *   Take a photo image of the current OpenGL window.  May have problems
1406  *   if window is partially obscured, either by other windows or by the
1407  *   edges of the display.
1408  */
1409 int
Togl_TakePhoto(Togl * togl,Tk_PhotoHandle photo)1410 Togl_TakePhoto(Togl *togl, Tk_PhotoHandle photo)
1411 {
1412     GLubyte *buffer;
1413     Tk_PhotoImageBlock photoBlock;
1414     int     y, midy;
1415     unsigned char *cp;
1416     int     width = togl->Width, height = togl->Height;
1417 
1418     /*
1419      * TIP #116 altered Tk_PhotoPutBlock API to add interp arg that 8.4
1420      * doesn't have.
1421      * We need to remove that for compiling with 8.4.
1422      */
1423 #if (TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION < 5)
1424 #  define TK_PHOTOPUTBLOCK(interp, hdl, blk, x, y, w, h, cr) \
1425    		Tk_PhotoPutBlock(hdl, blk, x, y, w, h, cr)
1426 #else
1427 #  define TK_PHOTOPUTBLOCK	Tk_PhotoPutBlock
1428 #endif
1429     buffer = (GLubyte *) ckalloc(width * height * 4);
1430     photoBlock.pixelPtr = buffer;
1431     photoBlock.width = width;
1432     photoBlock.height = height;
1433     photoBlock.pitch = width * 4;
1434     photoBlock.pixelSize = 4;
1435     photoBlock.offset[0] = 0;
1436     photoBlock.offset[1] = 1;
1437     photoBlock.offset[2] = 2;
1438     photoBlock.offset[3] = 3;
1439 
1440     if (!togl->RgbaFlag) {
1441 #if defined(TOGL_WGL)
1442         /* Due to the lack of a unique inverse mapping from the frame buffer to
1443          * the logical palette we need a translation map from the complete
1444          * logical palette. */
1445         int     n, i;
1446         TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin);
1447         LPPALETTEENTRY entry = (LPPALETTEENTRY) malloc(togl->MapSize *
1448                 sizeof (PALETTEENTRY));
1449 
1450         n = GetPaletteEntries(cmap->palette, 0, togl->MapSize, entry);
1451         for (i = 0; i < n; i++) {
1452             togl->RedMap[i] = (GLfloat) (entry[i].peRed / 255.0);
1453             togl->GreenMap[i] = (GLfloat) (entry[i].peGreen / 255.0);
1454             togl->BlueMap[i] = (GLfloat) (entry[i].peBlue / 255.0);
1455         }
1456         free(entry);
1457 #endif /* TOGL_WGL */
1458 
1459         glPixelMapfv(GL_PIXEL_MAP_I_TO_R, togl->MapSize, togl->RedMap);
1460         glPixelMapfv(GL_PIXEL_MAP_I_TO_G, togl->MapSize, togl->GreenMap);
1461         glPixelMapfv(GL_PIXEL_MAP_I_TO_B, togl->MapSize, togl->BlueMap);
1462     }
1463 
1464     glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
1465     glPixelStorei(GL_PACK_ALIGNMENT, 4);        /* guarantee performance */
1466     glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE);
1467     glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
1468 
1469 #if 1
1470     glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1471     glPixelStorei(GL_PACK_SKIP_ROWS, 0);
1472     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
1473     /* Some OpenGL drivers are buggy and return zero for Alpha instead of one
1474      * for RGB pixel formats.  If that is happening to you, upgrade your
1475      * graphics driver. */
1476 
1477     /* OpenGL's origin is bottom-left, Tk Photo image's is top-left, so mirror
1478      * the rows around the middle row. */
1479     midy = height / 2;
1480     cp = buffer;
1481     for (y = 0; y < midy; ++y) {
1482         int     m_y = height - 1 - y;   /* mirror y */
1483         unsigned char *m_cp = buffer + m_y * photoBlock.pitch;
1484         int     x;
1485 
1486         for (x = 0; x < photoBlock.pitch; ++x) {
1487             unsigned char c = *cp;
1488 
1489             *cp++ = *m_cp;
1490             *m_cp++ = c;
1491         }
1492     }
1493 #else
1494     /* OpenGL's origin is bottom-left, Tk Photo image's is top-left, so save
1495      * rows in reverse order. */
1496     glPixelStorei(GL_PACK_ROW_LENGTH, width);
1497     glPixelStorei(GL_PACK_SKIP_ROWS, -1);
1498     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
1499             buffer + width * (height - 1) * 4);
1500 #endif
1501 
1502     TK_PHOTOPUTBLOCK(togl->Interp, photo, &photoBlock, 0, 0, width, height,
1503             TK_PHOTO_COMPOSITE_SET);
1504 
1505     glPopClientAttrib();        /* restore PACK_ALIGNMENT */
1506     ckfree((char *) buffer);
1507     return TCL_OK;
1508 }
1509 
1510 Bool
Togl_SwapInterval(const Togl * togl,int interval)1511 Togl_SwapInterval(const Togl *togl, int interval)
1512 {
1513 #ifdef TOGL_AGL
1514     GLint   swapInterval = interval;
1515 
1516     return aglSetInteger(togl->Ctx, AGL_SWAP_INTERVAL, &swapInterval);
1517 #endif
1518 #ifdef TOGL_NSOPENGL
1519     GLint   swapInterval = interval;
1520     [togl->Ctx setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
1521     return True;
1522 #endif
1523 #ifdef TOGL_WGL
1524     typedef BOOL (WINAPI *BOOLFuncInt) (int);
1525     typedef const char *(WINAPI *StrFuncHDC) (HDC);
1526     static BOOLFuncInt swapInterval = NULL;
1527     static BOOL initialized = False;
1528 
1529     if (!initialized) {
1530         const char *extensions;
1531         StrFuncHDC getExtensionsString;
1532 
1533         getExtensionsString = (StrFuncHDC)
1534                 wglGetProcAddress("wglGetExtensionsStringARB");
1535         if (getExtensionsString == NULL)
1536             getExtensionsString = (StrFuncHDC)
1537                     wglGetProcAddress("wglGetExtensionsStringEXT");
1538         if (getExtensionsString) {
1539             extensions = getExtensionsString(togl->tglGLHdc);
1540             if (strstr(extensions, "WGL_EXT_swap_control") != NULL) {
1541                 swapInterval =
1542                         (BOOLFuncInt) wglGetProcAddress("wglSwapIntervalEXT");
1543             }
1544         }
1545         initialized = True;
1546     }
1547     if (swapInterval)
1548         return swapInterval(interval);
1549     return False;
1550 #endif
1551 #ifdef TOGL_X11
1552     typedef int (*IntFuncInt) (int);
1553     static IntFuncInt swapInterval = NULL;
1554     static int initialized = False;
1555 
1556     if (!initialized) {
1557         const char *extensions = glXQueryExtensionsString(togl->display,
1558                 Tk_ScreenNumber(togl->TkWin));
1559 
1560         if (strstr(extensions, "GLX_SGI_swap_control") != NULL) {
1561             swapInterval = (IntFuncInt) Togl_GetProcAddr("glXSwapIntervalSGI");
1562         } else if (strstr(extensions, "GLX_MESA_swap_control") != NULL) {
1563             swapInterval = (IntFuncInt) Togl_GetProcAddr("glXSwapIntervalMESA");
1564         }
1565         initialized = True;
1566     }
1567     if (swapInterval)
1568         return swapInterval(interval) == 0;
1569     return False;
1570 #endif
1571 }
1572 
1573 #if defined(TOGL_AGL)
1574 /* tell OpenGL which part of the Mac window to render to */
1575 static void
SetMacBufRect(Togl * togl)1576 SetMacBufRect(Togl *togl)
1577 {
1578     GLint   wrect[4];
1579     Rect    r;
1580     MacDrawable *d = ((TkWindow *) togl->TkWin)->privatePtr;
1581 
1582     /* set wrect[0,1] to lower left corner of widget */
1583     wrect[2] = Tk_Width(togl->TkWin);
1584     wrect[3] = Tk_Height(togl->TkWin);
1585     wrect[0] = d->xOff;
1586 
1587     GetPortBounds(Togl_MacOSXGetDrawablePort(togl), &r);
1588 
1589     wrect[1] = r.bottom - wrect[3] - d->yOff;
1590 
1591     if (togl->FullscreenFlag) {
1592         aglEnable(togl->Ctx, AGL_FS_CAPTURE_SINGLE);
1593         aglSetFullScreen(togl->Ctx, 0, 0, 0, 0);
1594     } else {
1595         aglUpdateContext(togl->Ctx);
1596     }
1597     aglSetInteger(togl->Ctx, AGL_BUFFER_RECT, wrect);
1598     aglEnable(togl->Ctx, AGL_BUFFER_RECT);
1599 }
1600 
1601 static void
ReconfigureCB(CGDirectDisplayID display,CGDisplayChangeSummaryFlags flags,void * closure)1602 ReconfigureCB(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags,
1603         void *closure)
1604 {
1605     /* Display reconfiguration callback. Documented as needed by Apple QA1209.
1606      * Updated for 10.3 (and later) to use
1607      * CGDisplayRegisterReconfigurationCallback. */
1608     Togl   *togl = (Togl *) closure;
1609 
1610     if (0 != (flags & kCGDisplayBeginConfigurationFlag))
1611         return;                 /* wait until display is reconfigured */
1612 
1613     SetMacBufRect(togl);
1614     Togl_MakeCurrent(togl);
1615     if (togl->Ctx) {
1616         if (togl->ReshapeProc) {
1617             Togl_CallCallback(togl, togl->ReshapeProc);
1618         } else {
1619 	    Togl_SetViewPort(togl);
1620         }
1621     }
1622 }
1623 #endif
1624 
1625 #if defined(TOGL_NSOPENGL)
1626 /*
1627 TODO: It appears that Tk only makes an NSView for toplevel windows.
1628 Also it looks like NSOpenGL does not have the equivalent of AGL_BUFFER_RECT
1629 that allows opengl drawing to just part of an NSView.  So we might need to
1630 create our own NSView for controlling the opengl bounds.
1631 Look at TkMacOSXMakeRealWindowExist() in tkMacOSXWm.c.
1632 */
1633 
1634 /* tell OpenGL which part of the Mac window to render to */
1635 static void
SetMacBufRect(Togl * togl)1636 SetMacBufRect(Togl *togl)
1637 {
1638     Rect r, rt;
1639     NSRect    rect;
1640     TkWindow *w = (TkWindow *) togl->TkWin;
1641     TkWindow *t = w->privatePtr->toplevel->winPtr;
1642 
1643     TkMacOSXWinBounds(w, &r);
1644     TkMacOSXWinBounds(t, &rt);
1645 
1646     rect.origin.x = r.left - rt.left;
1647     rect.origin.y = rt.bottom - r.bottom;
1648     rect.size.width = r.right - r.left;
1649     rect.size.height = r.bottom - r.top;
1650 
1651     [togl->nsview setFrame:rect];
1652     [togl->Ctx update];
1653 
1654     /* TODO: Support full screen. */
1655 }
1656 
1657 static void
ReconfigureCB(CGDirectDisplayID display,CGDisplayChangeSummaryFlags flags,void * closure)1658 ReconfigureCB(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags,
1659         void *closure)
1660 {
1661     /* Display reconfiguration callback. Documented as needed by Apple QA1209.
1662      * Updated for 10.3 (and later) to use
1663      * CGDisplayRegisterReconfigurationCallback. */
1664     Togl   *togl = (Togl *) closure;
1665 
1666     if (0 != (flags & kCGDisplayBeginConfigurationFlag))
1667         return;                 /* wait until display is reconfigured */
1668 
1669     SetMacBufRect(togl);
1670     Togl_MakeCurrent(togl);
1671     if (togl->Ctx) {
1672         if (togl->ReshapeProc) {
1673             Togl_CallCallback(togl, togl->ReshapeProc);
1674         } else {
1675             Togl_SetViewPort(togl);
1676         }
1677     }
1678 }
1679 #endif
1680 
1681 /*
1682  * Called when the widget's contents must be redrawn.  Basically, we
1683  * just call the user's render callback function.
1684  *
1685  * Note that the parameter type is ClientData so this function can be
1686  * passed to Tk_DoWhenIdle().
1687  */
1688 static void
Togl_Render(ClientData clientData)1689 Togl_Render(ClientData clientData)
1690 {
1691     Togl   *togl = (Togl *) clientData;
1692 
1693     if (togl->DisplayProc) {
1694         Togl_MakeCurrent(togl);
1695         Togl_CallCallback(togl, togl->DisplayProc);
1696     }
1697     togl->UpdatePending = False;
1698 }
1699 
1700 
1701 static void
Togl_RenderOverlay(ClientData clientData)1702 Togl_RenderOverlay(ClientData clientData)
1703 {
1704     Togl   *togl = (Togl *) clientData;
1705 
1706     if (togl->OverlayFlag && togl->OverlayDisplayProc) {
1707 #if defined(TOGL_WGL)
1708         int     res = wglMakeCurrent(togl->tglGLHdc, togl->tglGLOverlayHglrc);
1709 
1710         if (!res) {
1711             ErrorExit(TEXT("wglMakeCurrent overlay"));
1712         }
1713 #elif defined(TOGL_X11)
1714         (void) glXMakeCurrent(Tk_Display(togl->TkWin),
1715                 togl->OverlayWindow, togl->OverlayCtx);
1716 #endif /* TOGL_WGL */
1717 
1718         Togl_CallCallback(togl, togl->OverlayDisplayProc);
1719     }
1720     togl->OverlayUpdatePending = False;
1721 }
1722 
1723 
1724 static int
Togl_EnterStereo(Togl * togl)1725 Togl_EnterStereo(Togl *togl)
1726 {
1727     if (togl->Stereo == TOGL_STEREO_ROW_INTERLEAVED) {
1728         GLint   stencil_bits;
1729         Tk_Window top;
1730 
1731         Togl_MakeCurrent(togl);
1732         glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
1733         if (stencil_bits == 0) {
1734             Tcl_SetResult(togl->Interp,
1735                     TCL_STUPID "need stencil buffer for row interleaved stereo",
1736                     TCL_STATIC);
1737             return False;
1738         }
1739         togl->riStencilBit = 1u << (stencil_bits - 1);
1740         glEnable(GL_STENCIL_TEST);
1741 
1742         /* Need to redraw window when moved between odd and even scanlines, so
1743          * bind to top level window so we're notified when that happens. */
1744         top = togl->TkWin;
1745         while (!Tk_IsTopLevel(top)) {
1746             top = Tk_Parent(top);
1747             if (top == NULL)
1748                 break;
1749         }
1750         if (top) {
1751             Tk_CreateEventHandler(top, StructureNotifyMask, Togl_RedisplayProc,
1752                     (ClientData) togl);
1753         }
1754     }
1755     return True;
1756 }
1757 
1758 
1759 static void
Togl_LeaveStereo(Togl * togl,int oldStereo)1760 Togl_LeaveStereo(Togl *togl, int oldStereo)
1761 {
1762     switch (oldStereo) {
1763       default:
1764           break;
1765 #ifdef HAVE_AUTOSTEREO
1766       case TOGL_STEREO_NATIVE:
1767           if (togl->ash != -1) {
1768               ASClosedStereoWindow(togl->ash);
1769               togl->ash = -1;
1770           }
1771           break;
1772 #endif
1773 #ifdef __sgi
1774       case TOGL_STEREO_SGIOLDSTYLE:
1775           togl->currentStereoBuffer = STEREO_BUFFER_NONE;
1776           glXWaitGL();          /* sync with GL command stream before calling X
1777                                  */
1778           XSGISetStereoBuffer(togl->display, Tk_WindowId(togl->TkWin),
1779                   togl->currentStereoBuffer);
1780           glXWaitX();           /* sync with X command stream before calling GL
1781                                  */
1782           break;
1783 #endif
1784       case TOGL_STEREO_ROW_INTERLEAVED:
1785           if (togl->riStencilBit) {
1786               Tk_Window top;
1787 
1788               glDisable(GL_STENCIL_TEST);
1789 
1790               /* need to remove previously added top level event handler */
1791               top = togl->TkWin;
1792               while (!Tk_IsTopLevel(top)) {
1793                   top = Tk_Parent(top);
1794                   if (top == NULL)
1795                       break;
1796               }
1797               if (top) {
1798                   Tk_DeleteEventHandler(top, StructureNotifyMask,
1799                           Togl_RedisplayProc, (ClientData) togl);
1800               }
1801           }
1802           break;
1803     }
1804 }
1805 
1806 
1807 /*
1808  * See domentation about what can't be changed
1809  */
1810 static int
Togl_ObjConfigure(Tcl_Interp * interp,Togl * togl,int objc,Tcl_Obj * const * objv)1811 Togl_ObjConfigure(Tcl_Interp *interp, Togl *togl,
1812         int objc, Tcl_Obj *const *objv)
1813 {
1814     Tk_SavedOptions savedOptions;
1815     int     error, mask;
1816     int     undoMask = 0;
1817     Tcl_Obj *errorResult = NULL;
1818     int     oldStereo = togl->Stereo;
1819     int     oldWidth = togl->Width;
1820     int     oldHeight = togl->Height;
1821 
1822     for (error = 0; error <= 1; ++error, mask = undoMask) {
1823         if (error == 0) {
1824             /*
1825              * Tk_SetOptions parses the command arguments
1826              * and looks for defaults in the resource database.
1827              */
1828             if (Tk_SetOptions(interp, WIDGREC togl, togl->tpg->optionTable,
1829                             objc, objv, togl->TkWin, &savedOptions, &mask)
1830                     != TCL_OK) {
1831                 /* previous values are restored, so nothing to do */
1832                 return TCL_ERROR;
1833             }
1834         } else {
1835             /*
1836              * Restore options from saved values
1837              */
1838             errorResult = Tcl_GetObjResult(interp);
1839             Tcl_IncrRefCount(errorResult);
1840             Tk_RestoreSavedOptions(&savedOptions);
1841         }
1842 
1843         if (togl->Ident && togl->Ident[0] == '.') {
1844             Tcl_AppendResult(interp, "Can not set ident to a window path name",
1845                     NULL);
1846             continue;
1847         }
1848 
1849         if (togl->FullscreenFlag) {
1850             /* override width and height */
1851             togl->Width = WidthOfScreen(Tk_Screen(togl->TkWin));
1852             togl->Height = HeightOfScreen(Tk_Screen(togl->TkWin));
1853             undoMask |= GEOMETRY_MASK;
1854         }
1855 
1856         if (mask & GEOMETRY_MASK) {
1857             if (!togl->PbufferFlag) {
1858                 Togl_WorldChanged((ClientData) togl);
1859                 /* Reset width and height so ConfigureNotify
1860                  * event will call reshape callback */
1861                 togl->Width = oldWidth;
1862                 togl->Height = oldHeight;
1863                 undoMask |= GEOMETRY_MASK;
1864             }
1865         }
1866 
1867         if (mask & OVERLAY_MASK) {
1868 #if !TOGL_USE_OVERLAY
1869             if (togl->OverlayFlag) {
1870                 Tcl_AppendResult(interp, "Sorry, overlay was disabled", NULL);
1871                 continue;
1872             }
1873 #else
1874 #  if defined(TOGL_X11)
1875             if (togl->OverlayCtx)
1876 #  elif defined(TOGL_WGL)
1877             if (togl->tglGLOverlayHglrc)
1878 #  endif
1879             {
1880                 /*
1881                  * Trying to change existing pixel format/graphics context
1882                  */
1883                 Tcl_AppendResult(interp,
1884                         "Unable to change overlay pixel format", NULL);
1885                 continue;
1886             }
1887 #endif
1888         }
1889 
1890         if (mask & SWAP_MASK) {
1891             if (togl->Ctx) {
1892                 /*
1893                  * Change existing swap interval
1894                  */
1895                 Togl_MakeCurrent(togl); /* TODO: needed? */
1896                 Togl_SwapInterval(togl, togl->SwapInterval);
1897                 undoMask |= SWAP_MASK;
1898             }
1899         }
1900 
1901         if (error == 0 && (mask & STEREO_FORMAT_MASK) != 0) {
1902             if (oldStereo == TOGL_STEREO_NATIVE
1903                     || togl->Stereo == TOGL_STEREO_NATIVE) {
1904                 /* only native stereo affects the visual format */
1905                 mask |= FORMAT_MASK;
1906             }
1907             if (togl->Stereo == TOGL_STEREO_SGIOLDSTYLE) {
1908 #ifndef __sgi
1909                 Tcl_AppendResult(interp,
1910                         "sgioldstyle: only available on SGI computers", NULL);
1911                 continue;
1912 #else
1913                 int     event, error;
1914 
1915                 /* Make sure Display supports SGIStereo */
1916                 if (XSGIStereoQueryExtension(Tk_Display(togl->TkWin), &event,
1917                                 &error) == False) {
1918                     Tcl_AppendResult(interp,
1919                             "sgioldstyle: SGIStereo X extension is missing",
1920                             NULL);
1921                     continue;
1922                 }
1923                 /* Make sure Window (Screen) supports SGIStereo */
1924                 if (XSGIQueryStereoMode(Tk_Display(togl->TkWin),
1925                                 Tk_WindowId(Tk_Parent(togl->TkWin))) ==
1926                         X_STEREO_UNSUPPORTED) {
1927                     Tcl_AppendResult(interp,
1928                             "sgioldstyle: unsupported by screen", NULL);
1929                     continue;
1930                 }
1931 #endif
1932             }
1933         }
1934 
1935         if (mask & FORMAT_MASK) {
1936             if (togl->Ctx) {
1937                 /*
1938                  * Trying to change existing pixel format/graphics context
1939                  * TODO: (re)create graphics context
1940                  *
1941                  * save old graphics context
1942                  * try to create new one and share display lists
1943                  * if failure, then restore old one
1944                  */
1945                 Tcl_AppendResult(interp, "Unable to change pixel format", NULL);
1946                 continue;
1947             }
1948             if (togl->ShareContext && togl->ShareList) {
1949                 Tcl_AppendResult(interp,
1950                         "only one of -sharelist and -sharecontext allowed",
1951                         NULL);
1952                 continue;
1953             }
1954             if (togl->PbufferFlag && togl->Stereo) {
1955                 Tcl_AppendResult(interp, "pbuffer not supported with stereo",
1956                         NULL);
1957                 continue;
1958             }
1959             if (togl->PbufferFlag && togl->OverlayFlag) {
1960                 Tcl_AppendResult(interp, "pbuffer not supported with overlay",
1961                         NULL);
1962                 continue;
1963             }
1964             if (togl->FullscreenFlag) {
1965 #if defined(TOGL_NSOPENGL)
1966 	        Tcl_AppendResult(interp,
1967                        "Fullscreen not supported with Cocoa Tk", NULL);
1968                 continue;
1969 #endif
1970 #ifndef TOGL_AGL
1971 #  if TK_MAJOR_VERSION < 8 || (TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 5)
1972                 Tcl_AppendResult(interp,
1973                         "Need Tk 8.5 or later for fullscreen support", NULL);
1974                 continue;
1975 #  endif
1976 #endif
1977             }
1978             /* Whether or not the format is okay is figured out when togl tries
1979              * to create the window. */
1980 #ifdef MESA_COLOR_HACK
1981             free_default_color_cells(Tk_Display(togl->TkWin),
1982                     Tk_Colormap(togl->TkWin));
1983 #endif
1984             undoMask |= FORMAT_MASK;
1985         }
1986 
1987         if (togl->Ctx) {
1988             if (oldStereo != togl->Stereo) {
1989                 /* leaving stereo */
1990                 Togl_LeaveStereo(togl, oldStereo);
1991                 if (togl->Stereo && !Togl_EnterStereo(togl))
1992                     continue;
1993             }
1994         }
1995 
1996         if (mask & TIMER_MASK) {
1997             if (togl->timerHandler != NULL) {
1998                 Tcl_DeleteTimerHandler(togl->timerHandler);
1999             }
2000             if (togl->TimerProc) {
2001                 togl->timerHandler =
2002                         Tcl_CreateTimerHandler(togl->TimerInterval, Togl_Timer,
2003                         (ClientData) togl);
2004             }
2005             undoMask |= TIMER_MASK;
2006         }
2007         break;
2008     }
2009 
2010     if (error == 0) {
2011         Tk_FreeSavedOptions(&savedOptions);
2012         return TCL_OK;
2013     } else {
2014         Tcl_SetObjResult(interp, errorResult);
2015         Tcl_DecrRefCount(errorResult);
2016         return TCL_ERROR;
2017     }
2018 }
2019 
2020 
2021 static int
Togl_ObjWidget(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)2022 Togl_ObjWidget(ClientData clientData, Tcl_Interp *interp, int objc,
2023         Tcl_Obj *const *objv)
2024 {
2025     Togl   *togl = (Togl *) clientData;
2026     const char *commands[] = {
2027         "cget", "configure", "extensions",
2028         "postredisplay", "render",
2029         "swapbuffers", "makecurrent", "takephoto",
2030         "loadbitmapfont", "unloadbitmapfont", "write",
2031         "uselayer", "showoverlay", "hideoverlay",
2032         "postredisplayoverlay", "renderoverlay",
2033         "existsoverlay", "ismappedoverlay",
2034         "getoverlaytransparentvalue",
2035         "drawbuffer", "clear", "frustum", "ortho",
2036         "numeyes", "contexttag", "copycontextto", "maketopfortrackpadevents",
2037         NULL
2038     };
2039     enum command
2040     {
2041         TOGL_CGET, TOGL_CONFIGURE, TOGL_EXTENSIONS,
2042         TOGL_POSTREDISPLAY, TOGL_RENDER,
2043         TOGL_SWAPBUFFERS, TOGL_MAKECURRENT, TOGL_TAKEPHOTO,
2044         TOGL_LOADBITMAPFONT, TOGL_UNLOADBITMAPFONT, TOGL_WRITE,
2045         TOGL_USELAYER, TOGL_SHOWOVERLAY, TOGL_HIDEOVERLAY,
2046         TOGL_POSTREDISPLAYOVERLAY, TOGL_RENDEROVERLAY,
2047         TOGL_EXISTSOVERLAY, TOGL_ISMAPPEDOVERLAY,
2048         TOGL_GETOVERLAYTRANSPARENTVALUE,
2049         TOGL_DRAWBUFFER, TOGL_CLEAR, TOGL_FRUSTUM, TOGL_ORTHO,
2050         TOGL_NUMEYES, TOGL_CONTEXTTAG, TOGL_COPYCONTEXTTO, TOGL_MAKETOPFORTRACKPADEVENTS
2051     };
2052     int     result = TCL_OK;
2053     Tcl_Obj *objPtr;
2054     int     index;
2055 
2056     if (objc < 2) {
2057         Tcl_WrongNumArgs(interp, 1, objv, "command ?arg arg ...?");
2058         return TCL_ERROR;
2059     }
2060 
2061     Tk_Preserve((ClientData) togl);
2062 
2063     result = Tcl_GetIndexFromObj(interp, objv[1], commands, "option", 0,
2064             &index);
2065 
2066     switch (index) {
2067       case TOGL_CGET:
2068           if (objc != 3) {
2069               Tcl_WrongNumArgs(interp, 2, objv, "option");
2070               result = TCL_ERROR;
2071               break;
2072           }
2073           objPtr = Tk_GetOptionValue(interp, WIDGREC togl,
2074                   togl->tpg->optionTable, (objc == 3) ? objv[2] : NULL,
2075                   togl->TkWin);
2076           if (objPtr == NULL) {
2077               result = TCL_ERROR;
2078               break;
2079           }
2080           Tcl_SetObjResult(interp, objPtr);
2081           break;
2082 
2083       case TOGL_CONFIGURE:
2084           if (objc <= 3) {
2085               /*
2086                * Return one item if the option is given,
2087                * or return all configuration information
2088                */
2089               objPtr = Tk_GetOptionInfo(interp, WIDGREC togl,
2090                       togl->tpg->optionTable, (objc == 3) ? objv[2] : NULL,
2091                       togl->TkWin);
2092               if (objPtr == NULL) {
2093                   result = TCL_ERROR;
2094               } else {
2095                   Tcl_SetObjResult(interp, objPtr);
2096               }
2097           } else {
2098               /* Execute a configuration change */
2099               result = Togl_ObjConfigure(interp, togl, objc - 2, objv + 2);
2100           }
2101           break;
2102 
2103       case TOGL_EXTENSIONS:
2104           /* Return a list of OpenGL extensions available */
2105           /* TODO: -glu for glu extensions, -platform for glx/wgl extensions */
2106           if (objc == 2) {
2107               const char *extensions;
2108               Tcl_Obj *objPtr;
2109               int     length = -1;
2110 
2111               extensions = (const char *) glGetString(GL_EXTENSIONS);
2112               objPtr = Tcl_NewStringObj(extensions, -1);
2113               /* convert to list by asking for its length */
2114               (void) Tcl_ListObjLength(interp, objPtr, &length);
2115               Tcl_SetObjResult(interp, objPtr);
2116           } else {
2117               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2118               result = TCL_ERROR;
2119           }
2120           break;
2121 
2122       case TOGL_POSTREDISPLAY:
2123           /* schedule the widget to be redrawn */
2124           if (objc == 2) {
2125               Togl_PostRedisplay(togl);
2126           } else {
2127               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2128               result = TCL_ERROR;
2129           }
2130           break;
2131 
2132       case TOGL_RENDER:
2133           /* force the widget to be redrawn */
2134           if (objc == 2) {
2135               Togl_Render((ClientData) togl);
2136           } else {
2137               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2138               result = TCL_ERROR;
2139           }
2140           break;
2141 
2142       case TOGL_SWAPBUFFERS:
2143           /* force the widget to be redrawn */
2144           if (objc == 2) {
2145               Togl_SwapBuffers(togl);
2146           } else {
2147               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2148               result = TCL_ERROR;
2149           }
2150           break;
2151 
2152       case TOGL_MAKECURRENT:
2153           /* force the widget to be redrawn */
2154           if (objc == 2) {
2155               Togl_MakeCurrent(togl);
2156           } else {
2157               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2158               result = TCL_ERROR;
2159           }
2160           break;
2161 
2162       case TOGL_TAKEPHOTO:
2163       {
2164           /* force the widget to be redrawn */
2165           if (objc != 3) {
2166               Tcl_WrongNumArgs(interp, 2, objv, "name");
2167               result = TCL_ERROR;
2168           } else {
2169               const char *name;
2170               Tk_PhotoHandle photo;
2171 
2172               name = Tcl_GetStringFromObj(objv[2], NULL);
2173               photo = Tk_FindPhoto(interp, name);
2174               if (photo == NULL) {
2175                   Tcl_AppendResult(interp, "image \"", name,
2176                           "\" doesn't exist or is not a photo image", NULL);
2177                   result = TCL_ERROR;
2178                   break;
2179               }
2180               glPushAttrib(GL_PIXEL_MODE_BIT);
2181               if (togl->DoubleFlag) {
2182                   glReadBuffer(GL_FRONT);
2183               }
2184               Togl_TakePhoto(togl, photo);
2185               glPopAttrib();    /* restore glReadBuffer */
2186           }
2187           break;
2188       }
2189 
2190       case TOGL_LOADBITMAPFONT:
2191 #if TOGL_USE_FONTS != 1
2192           Tcl_AppendResult(interp, "unsupported", NULL);
2193           result = TCL_ERROR;
2194 #else
2195           if (objc >= 3) {
2196               Tcl_Obj *font, *list;
2197 
2198               list = Tcl_NewListObj(objc - 2, objv + 2);
2199               Tcl_IncrRefCount(list);
2200               font = Togl_LoadBitmapFont(togl, Tcl_GetString(list));
2201               Tcl_DecrRefCount(list);
2202               if (font) {
2203                   Tcl_SetObjResult(interp, font);
2204                   result = TCL_OK;
2205               } else {
2206                   Tcl_AppendResult(interp, "Could not allocate font", NULL);
2207                   result = TCL_ERROR;
2208               }
2209           } else {
2210               Tcl_WrongNumArgs(interp, 2, objv, "fontname");
2211               result = TCL_ERROR;
2212           }
2213 #endif
2214           break;
2215 
2216       case TOGL_UNLOADBITMAPFONT:
2217 #if TOGL_USE_FONTS != 1
2218           Tcl_AppendResult(interp, "unsupported", NULL);
2219           result = TCL_ERROR;
2220 #else
2221           if (objc == 3) {
2222               result = Togl_UnloadBitmapFont(togl, objv[2]);
2223           } else {
2224               Tcl_WrongNumArgs(interp, 2, objv, "toglfont");
2225               result = TCL_ERROR;
2226           }
2227 #endif
2228           break;
2229 
2230       case TOGL_WRITE:{
2231 #if TOGL_USE_FONTS != 1
2232           Tcl_AppendResult(interp, "unsupported", NULL);
2233           result = TCL_ERROR;
2234 #else
2235           /* Tcl_Obj *toglfont = objv[2]; */
2236           int     wobjc = objc - 3;
2237           Tcl_Obj *const *wobjv = objv + 3;
2238 
2239           while (wobjc > 1) {
2240               const char *name = Tcl_GetStringFromObj(wobjv[0], NULL);
2241               int     oc, i;
2242               Tcl_Obj **ov;
2243               double  args[4];
2244 
2245               if (Tcl_ListObjGetElements(NULL, wobjv[1], &oc, &ov) != TCL_OK) {
2246                   oc = 0;
2247               } else if (oc <= 4) {
2248                   for (i = 0; i < oc; ++i) {
2249                       if (Tcl_GetDoubleFromObj(NULL, ov[i], &args[i]) != TCL_OK) {
2250                       }
2251                   }
2252               }
2253               if (strcmp(name, "-color") == 0) {
2254                   if (oc == 4)
2255                       glColor4d(args[0], args[1], args[2], args[3]);
2256                   else if (oc == 3)
2257                       glColor3d(args[0], args[1], args[2]);
2258                   else
2259                       goto write_usage;
2260               } else if (strcmp(name, "-pos") == 0) {
2261                   if (oc == 4)
2262                       glRasterPos4d(args[0], args[1], args[2], args[3]);
2263                   else if (oc == 3)
2264                       glRasterPos3d(args[0], args[1], args[2]);
2265                   else if (oc == 2)
2266                       glRasterPos2d(args[0], args[1]);
2267                   else
2268                       goto write_usage;
2269               } else
2270                   goto write_usage;
2271               wobjc -= 2;
2272               wobjv += 2;
2273           }
2274           if (wobjc != 1)
2275               goto write_usage;
2276           result = Togl_WriteObj(togl, objv[2], wobjv[0]);
2277           if (result != -1)
2278               result = TCL_OK;
2279           else {
2280               Tcl_AppendResult(interp, "togl write failed", NULL);
2281               result = TCL_ERROR;
2282           }
2283           break;
2284         write_usage:
2285           Tcl_WrongNumArgs(interp, 2, objv, "[-pos {x y [z [w]]}]"
2286                   " [-color {r g b [a]}" " string");
2287           result = TCL_ERROR;
2288 #endif
2289           break;
2290       }
2291 
2292       case TOGL_USELAYER:
2293           if (objc == 3) {
2294               int     layer;
2295 
2296               result = Tcl_GetIntFromObj(interp, objv[2], &layer);
2297               if (result == TCL_OK) {
2298                   Togl_UseLayer(togl, layer);
2299               }
2300           } else {
2301               Tcl_WrongNumArgs(interp, 2, objv, "layer");
2302               result = TCL_ERROR;
2303           }
2304           break;
2305 
2306       case TOGL_SHOWOVERLAY:
2307           if (objc == 2) {
2308               Togl_ShowOverlay(togl);
2309           } else {
2310               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2311               result = TCL_ERROR;
2312           }
2313           break;
2314 
2315       case TOGL_HIDEOVERLAY:
2316           if (objc == 2) {
2317               Togl_HideOverlay(togl);
2318           } else {
2319               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2320               result = TCL_ERROR;
2321           }
2322           break;
2323 
2324       case TOGL_POSTREDISPLAYOVERLAY:
2325           if (objc == 2) {
2326               Togl_PostOverlayRedisplay(togl);
2327           } else {
2328               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2329               result = TCL_ERROR;
2330           }
2331           break;
2332 
2333       case TOGL_RENDEROVERLAY:
2334           /* force the overlay to be redrawn */
2335           if (objc == 2) {
2336               Togl_RenderOverlay((ClientData) togl);
2337           } else {
2338               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2339               result = TCL_ERROR;
2340           }
2341           break;
2342 
2343       case TOGL_EXISTSOVERLAY:
2344           if (objc == 2) {
2345               Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_ExistsOverlay(togl)));
2346           } else {
2347               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2348               result = TCL_ERROR;
2349           }
2350           break;
2351 
2352       case TOGL_ISMAPPEDOVERLAY:
2353           if (objc == 2) {
2354               Tcl_SetObjResult(interp,
2355                       Tcl_NewIntObj(Togl_IsMappedOverlay(togl)));
2356           } else {
2357               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2358               result = TCL_ERROR;
2359           }
2360           break;
2361 
2362       case TOGL_GETOVERLAYTRANSPARENTVALUE:
2363           if (objc == 2) {
2364               Tcl_SetObjResult(interp,
2365                       Tcl_NewIntObj(Togl_GetOverlayTransparentValue(togl)));
2366           } else {
2367               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2368               result = TCL_ERROR;
2369           }
2370           break;
2371 
2372       case TOGL_DRAWBUFFER:
2373           if (objc != 3) {
2374               Tcl_WrongNumArgs(interp, 2, objv, "mode");
2375               result = TCL_ERROR;
2376           } else {
2377               int     mask;
2378 
2379               result = Tcl_GetIntFromObj(interp, objv[2], &mask);
2380               if (result == TCL_ERROR)
2381                   break;
2382               Togl_DrawBuffer(togl, (GLenum) mask);
2383           }
2384           break;
2385 
2386       case TOGL_CLEAR:
2387           if (objc != 3) {
2388               Tcl_WrongNumArgs(interp, 2, objv, "mask");
2389               result = TCL_ERROR;
2390           } else {
2391               int     mask;
2392 
2393               result = Tcl_GetIntFromObj(interp, objv[2], &mask);
2394               if (result == TCL_ERROR)
2395                   break;
2396               Togl_Clear(togl, (GLbitfield) mask);
2397           }
2398           break;
2399 
2400       case TOGL_FRUSTUM:
2401           if (objc != 8) {
2402               Tcl_WrongNumArgs(interp, 2, objv,
2403                       "left right bottom top near far");
2404               result = TCL_ERROR;
2405           } else {
2406               double  left, right, bottom, top, zNear, zFar;
2407 
2408               if (Tcl_GetDoubleFromObj(interp, objv[2], &left) == TCL_ERROR
2409                       || Tcl_GetDoubleFromObj(interp, objv[3],
2410                               &right) == TCL_ERROR
2411                       || Tcl_GetDoubleFromObj(interp, objv[4],
2412                               &bottom) == TCL_ERROR
2413                       || Tcl_GetDoubleFromObj(interp, objv[5],
2414                               &top) == TCL_ERROR
2415                       || Tcl_GetDoubleFromObj(interp, objv[6],
2416                               &zNear) == TCL_ERROR
2417                       || Tcl_GetDoubleFromObj(interp, objv[7],
2418                               &zFar) == TCL_ERROR) {
2419                   result = TCL_ERROR;
2420                   break;
2421               }
2422               Togl_Frustum(togl, left, right, bottom, top, zNear, zFar);
2423           }
2424           break;
2425 
2426       case TOGL_ORTHO:
2427           if (objc != 8) {
2428               Tcl_WrongNumArgs(interp, 2, objv,
2429                       "left right bottom top near far");
2430               result = TCL_ERROR;
2431           } else {
2432               double  left, right, bottom, top, zNear, zFar;
2433 
2434               if (Tcl_GetDoubleFromObj(interp, objv[2], &left) == TCL_ERROR
2435                       || Tcl_GetDoubleFromObj(interp, objv[3],
2436                               &right) == TCL_ERROR
2437                       || Tcl_GetDoubleFromObj(interp, objv[4],
2438                               &bottom) == TCL_ERROR
2439                       || Tcl_GetDoubleFromObj(interp, objv[5],
2440                               &top) == TCL_ERROR
2441                       || Tcl_GetDoubleFromObj(interp, objv[6],
2442                               &zNear) == TCL_ERROR
2443                       || Tcl_GetDoubleFromObj(interp, objv[7],
2444                               &zFar) == TCL_ERROR) {
2445                   result = TCL_ERROR;
2446                   break;
2447               }
2448               Togl_Ortho(togl, left, right, bottom, top, zNear, zFar);
2449           }
2450           break;
2451 
2452       case TOGL_NUMEYES:
2453           if (objc == 2) {
2454               Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_NumEyes(togl)));
2455           } else {
2456               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2457               result = TCL_ERROR;
2458           }
2459           break;
2460 
2461       case TOGL_CONTEXTTAG:
2462           if (objc == 2) {
2463               Tcl_SetObjResult(interp, Tcl_NewIntObj(Togl_ContextTag(togl)));
2464           } else {
2465               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2466               result = TCL_ERROR;
2467           }
2468           break;
2469 
2470       case TOGL_COPYCONTEXTTO:
2471           if (objc != 4) {
2472               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2473               result = TCL_ERROR;
2474           } else {
2475               Togl   *to;
2476               unsigned int mask;
2477 
2478               if (Togl_GetToglFromObj(togl->Interp, objv[2], &to) == TCL_ERROR
2479                       || Tcl_GetIntFromObj(togl->Interp, objv[3],
2480                               (int *) &mask) == TCL_ERROR) {
2481                   result = TCL_ERROR;
2482                   break;
2483               }
2484               result = Togl_CopyContext(togl, to, mask);
2485           }
2486 #ifdef TOGL_NSOPENGL
2487     case TOGL_MAKETOPFORTRACKPADEVENTS:
2488           if (objc == 2) {
2489 	    // This hack places the Togl NSView at the top of sibling views so that it receives
2490 	    // trackpad events.  The hierarchy is not used for drawing, nor for mouse event dispatching.
2491 	    [togl->nsview retain];
2492 	    [togl->nsview removeFromSuperview];
2493 	    MacDrawable *d = ((TkWindow *) togl->TkWin)->privatePtr;
2494 	    NSView *topview = d->toplevel->view;
2495 	    [topview addSubview:togl->nsview];
2496 	    [togl->nsview release];
2497           } else {
2498               Tcl_WrongNumArgs(interp, 2, objv, NULL);
2499               result = TCL_ERROR;
2500           }
2501           break;
2502 #endif
2503     }
2504 
2505     Tk_Release((ClientData) togl);
2506     return result;
2507 }
2508 
2509 /*
2510  * Togl_ObjCmdDelete
2511  *
2512  * Called when togl command is removed from interpreter.
2513  */
2514 
2515 static void
Togl_ObjCmdDelete(ClientData clientData)2516 Togl_ObjCmdDelete(ClientData clientData)
2517 {
2518     if (clientData != NULL) {
2519         Togl_PackageGlobals *tpg = (Togl_PackageGlobals *) clientData;
2520 
2521         Tk_DeleteOptionTable(tpg->optionTable);
2522         ckfree((char *) clientData);
2523     }
2524 }
2525 
2526 
2527 /*
2528  * Togl_ObjCmd
2529  *
2530  *   Called when Togl is executed - creation of a Togl widget.
2531  *     * Creates a new window
2532  *     * Creates an 'Togl' data structure
2533  *     * Creates an event handler for this window
2534  *     * Creates a command that handles this object
2535  *     * Configures this Togl for the given arguments
2536  */
2537 int
Togl_ObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const * objv)2538 Togl_ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2539         Tcl_Obj *const *objv)
2540 {
2541     Togl_PackageGlobals *tpg;
2542     Togl   *togl;
2543     Tk_Window tkwin;
2544     Tcl_SavedResult saveError;
2545 
2546     if (objc <= 1) {
2547         Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
2548         return TCL_ERROR;
2549     }
2550     tpg = (Togl_PackageGlobals *) clientData;
2551     if (tpg == NULL) {
2552         Tcl_CmdInfo info;
2553         const char *name;
2554 
2555         /*
2556          * Initialize the Togl_PackageGlobals for this widget the
2557          * first time a Togl widget is created.  The globals are
2558          * saved as our client data.
2559          */
2560 
2561         tpg = (Togl_PackageGlobals *) ckalloc(sizeof (Togl_PackageGlobals));
2562         if (tpg == NULL) {
2563             return TCL_ERROR;
2564         }
2565         tpg->nextContextTag = 0;
2566         tpg->optionTable = Tk_CreateOptionTable(interp, optionSpecs);
2567         tpg->toglHead = NULL;
2568 
2569         name = Tcl_GetString(objv[0]);
2570         Tcl_GetCommandInfo(interp, name, &info);
2571         info.objClientData = (ClientData) tpg;
2572         Tcl_SetCommandInfo(interp, name, &info);
2573     }
2574 
2575     /* Create the window. */
2576     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
2577             Tcl_GetString(objv[1]), NULL);
2578     if (tkwin == NULL) {
2579         return TCL_ERROR;
2580     }
2581 
2582     Tk_SetClass(tkwin, "Togl");
2583 
2584     /* Create Togl data structure */
2585     togl = (Togl *) ckalloc(sizeof (Togl));
2586     if (togl == NULL) {
2587         return TCL_ERROR;
2588     }
2589 
2590     /* initialize Togl data structures values */
2591     togl->Next = NULL;
2592     togl->Ctx = NULL;
2593 #if defined(TOGL_WGL)
2594     togl->tglGLHdc = NULL;
2595     togl->tglGLOverlayHglrc = NULL;
2596 #elif defined(TOGL_X11)
2597     togl->OverlayCtx = NULL;
2598 #endif
2599     togl->contextTag = 0;
2600     togl->display = Tk_Display(tkwin);
2601     togl->TkWin = tkwin;
2602     togl->Interp = interp;
2603     togl->VisInfo = NULL;
2604     togl->OverlayWindow = None;
2605     togl->OverlayCmap = None;
2606     togl->OverlayTransparentPixel = 0;
2607     togl->OverlayIsMapped = False;
2608 
2609     togl->UpdatePending = False;
2610     togl->OverlayUpdatePending = False;
2611     togl->tpg = tpg;
2612     togl->Client_Data = NULL;
2613 
2614     /* for color index mode photos */
2615     togl->RedMap = togl->GreenMap = togl->BlueMap = NULL;
2616     togl->MapSize = 0;
2617 
2618 #ifndef NO_TK_CURSOR
2619     togl->Cursor = None;
2620 #endif
2621     togl->Width = 0;
2622     togl->Height = 0;
2623     togl->PixelScale = 1;
2624     togl->SetGrid = 0;
2625     togl->TimerInterval = 0;
2626     togl->RgbaFlag = True;
2627     togl->RgbaRed = 1;
2628     togl->RgbaGreen = 1;
2629     togl->RgbaBlue = 1;
2630     togl->DoubleFlag = False;
2631     togl->DepthFlag = False;
2632     togl->DepthSize = 1;
2633     togl->AccumFlag = False;
2634     togl->AccumRed = 1;
2635     togl->AccumGreen = 1;
2636     togl->AccumBlue = 1;
2637     togl->AccumAlpha = 1;
2638     togl->AlphaFlag = False;
2639     togl->AlphaSize = 1;
2640     togl->StencilFlag = False;
2641     togl->StencilSize = 1;
2642     togl->OverlayFlag = False;
2643     togl->Stereo = TOGL_STEREO_NONE;
2644     togl->EyeSeparation = 0;
2645     togl->Convergence = 0;
2646     togl->riStencilBit = 0;
2647     togl->AuxNumber = 0;
2648     togl->Indirect = False;
2649     togl->PixelFormat = 0;
2650     togl->SwapInterval = 1;
2651     togl->MultisampleFlag = False;
2652     togl->FullscreenFlag = False;
2653     togl->PbufferFlag = False;
2654     togl->LargestPbufferFlag = False;
2655 #if defined(TOGL_X11)
2656     togl->fbcfg = None;
2657     togl->pbuf = None;
2658 #elif defined(TOGL_WGL)
2659     togl->pbuf = None;
2660     togl->pbufferLost = 0;
2661 #elif defined(TOGL_AGL)
2662     togl->pbuf = NULL;
2663 #elif defined(TOGL_NSOPENGL)
2664     togl->pbuf = NULL;
2665 #endif
2666 
2667     togl->CreateProc = NULL;
2668     togl->DisplayProc = NULL;
2669     togl->ReshapeProc = NULL;
2670     togl->DestroyProc = NULL;
2671     togl->TimerProc = NULL;
2672     togl->timerHandler = NULL;
2673     togl->OverlayDisplayProc = NULL;
2674     togl->MagnifyProc = NULL;
2675     togl->RotateProc = NULL;
2676     togl->ScrollProc = NULL;
2677     togl->ScrollWheelProc = NULL;
2678     togl->TouchesProc = NULL;
2679     togl->ShareList = NULL;
2680     togl->ShareContext = NULL;
2681     togl->Ident = NULL;
2682     togl->PrivateCmapFlag = False;
2683     togl->currentStereoBuffer = STEREO_BUFFER_NONE;
2684 #ifdef HAVE_AUTOSTEREO
2685     togl->as_initialized = False;
2686     togl->ash = -1;
2687 #endif
2688     togl->badWindow = False;
2689 
2690     /* Create command event handler */
2691     togl->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(tkwin),
2692             Togl_ObjWidget, (ClientData) togl, ToglCmdDeletedProc);
2693 
2694     /*
2695      * Setup the Tk_ClassProcs callbacks to point at our own window creation
2696      * function
2697      *
2698      * We need to check at runtime if we should use the new Tk_SetClassProcs()
2699      * API or if we need to modify the window structure directly
2700      */
2701 #ifdef HAVE_TK_SETCLASSPROCS
2702 
2703     if (SetClassProcsPtr != NULL) {     /* use public API (Tk 8.4+) */
2704         Tk_ClassProcs *procsPtr;
2705 
2706         procsPtr = (Tk_ClassProcs *) ckalloc(sizeof (Tk_ClassProcs));
2707         procsPtr->size = sizeof (Tk_ClassProcs);
2708         procsPtr->createProc = Togl_MakeWindow;
2709         procsPtr->worldChangedProc = Togl_WorldChanged;
2710         procsPtr->modalProc = NULL;
2711         /* Tk_SetClassProcs(togl->TkWin, procsPtr, (ClientData) togl); */
2712         (SetClassProcsPtr) (togl->TkWin, procsPtr, (ClientData) togl);
2713     } else
2714 #endif
2715     {                           /* use private API */
2716         /*
2717          * We need to set these fields in the Tk_FakeWin structure: dummy17 =
2718          * classProcsPtr dummy18 = instanceData */
2719         TkClassProcs *procsPtr;
2720         Tk_FakeWin *winPtr = (Tk_FakeWin *) (togl->TkWin);
2721 
2722         procsPtr = (TkClassProcs *) ckalloc(sizeof (TkClassProcs));
2723         procsPtr->createProc = Togl_MakeWindow;
2724         procsPtr->geometryProc = Togl_WorldChanged;
2725         procsPtr->modalProc = NULL;
2726         winPtr->dummy17 = (char *) procsPtr;
2727         winPtr->dummy18 = (ClientData) togl;
2728     }
2729 
2730     Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
2731             Togl_EventProc, (ClientData) togl);
2732 
2733     /* Configure Togl widget */
2734     if (Tk_InitOptions(interp, WIDGREC togl, tpg->optionTable, tkwin) != TCL_OK
2735             || Togl_ObjConfigure(interp, togl, objc - 2, objv + 2) != TCL_OK) {
2736         goto error;
2737     }
2738 
2739     /*
2740      * If OpenGL window wasn't already created by Togl_ObjConfigure() we
2741      * create it now.  We can tell by checking if the OpenGL context has
2742      * been initialized.
2743      */
2744     if (!togl->Ctx) {
2745         Tk_MakeWindowExist(togl->TkWin);
2746         if (togl->badWindow) {
2747             goto error;
2748         }
2749     }
2750     Togl_MakeCurrent(togl);
2751     if (togl->contextTag == 0)
2752         togl->contextTag = ++tpg->nextContextTag;
2753 
2754     (void) Togl_SwapInterval(togl, togl->SwapInterval);
2755 
2756     /* If defined, call create callback */
2757     if (togl->CreateProc) {
2758         if (Togl_CallCallback(togl, togl->CreateProc) != TCL_OK) {
2759             goto error;
2760         }
2761     }
2762 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
2763     if (!togl->PbufferFlag)
2764         SetMacBufRect(togl);
2765 #endif
2766     /* If defined, call reshape proc */
2767     if (togl->ReshapeProc) {
2768         if (Togl_CallCallback(togl, togl->ReshapeProc) != TCL_OK) {
2769             goto error;
2770         }
2771     } else {
2772         Togl_SetViewPort(togl);
2773 #if defined(TOGL_X11)
2774         if (togl->OverlayFlag) {
2775             Togl_UseLayer(togl, TOGL_OVERLAY);
2776             Togl_SetViewPort(togl);
2777             Togl_UseLayer(togl, TOGL_NORMAL);
2778         }
2779 #endif
2780     }
2781 
2782     if (togl->Stereo && !Togl_EnterStereo(togl))
2783         goto error;
2784 
2785     Tcl_AppendResult(interp, Tk_PathName(tkwin), NULL);
2786 
2787     /* Add to linked list */
2788     AddToList(togl);
2789 
2790     return TCL_OK;
2791 
2792   error:
2793     Tcl_SaveResult(interp, &saveError);
2794     togl->badWindow = True;
2795     (void) Tcl_DeleteCommandFromToken(interp, togl->widgetCmd);
2796     Tcl_RestoreResult(interp, &saveError);
2797     Tcl_AppendResult(interp, "\nCouldn't configure togl widget", NULL);
2798     return TCL_ERROR;
2799 }
2800 
2801 
2802 #if TOGL_USE_OVERLAY
2803 
2804 /*
2805  * Do all the setup for overlay planes
2806  * Return:   TCL_OK or TCL_ERROR
2807  */
2808 static int
SetupOverlay(Togl * togl)2809 SetupOverlay(Togl *togl)
2810 {
2811 #  if defined(TOGL_X11)
2812 
2813 #    ifdef GLX_TRANSPARENT_TYPE_EXT
2814     static int ovAttributeList[] = {
2815         GLX_BUFFER_SIZE, 2,
2816         GLX_LEVEL, 1,
2817         GLX_TRANSPARENT_TYPE_EXT, GLX_TRANSPARENT_INDEX_EXT,
2818         None
2819     };
2820 #    else
2821     static int ovAttributeList[] = {
2822         GLX_BUFFER_SIZE, 2,
2823         GLX_LEVEL, 1,
2824         None
2825     };
2826 #    endif
2827 
2828     XVisualInfo *visinfo;
2829     TkWindow *winPtr = (TkWindow *) togl->TkWin;
2830 
2831     XSetWindowAttributes swa;
2832     Tcl_HashEntry *hPtr;
2833     int     new_flag;
2834 
2835     visinfo =
2836             glXChooseVisual(togl->display, Tk_ScreenNumber(winPtr),
2837             ovAttributeList);
2838     if (!visinfo) {
2839         Tcl_AppendResult(togl->Interp, Tk_PathName(winPtr),
2840                 ": No suitable overlay index visual available", NULL);
2841         togl->OverlayCtx = NULL;
2842         togl->OverlayWindow = 0;
2843         togl->OverlayCmap = 0;
2844         return TCL_ERROR;
2845     }
2846 #    ifdef GLX_TRANSPARENT_INDEX_EXT
2847     {
2848         int     fail = glXGetConfig(togl->display, visinfo,
2849                 GLX_TRANSPARENT_INDEX_VALUE_EXT,
2850                 &togl->OverlayTransparentPixel);
2851 
2852         if (fail)
2853             togl->OverlayTransparentPixel = 0;  /* maybe, maybe ... */
2854     }
2855 #    else
2856     togl->OverlayTransparentPixel = 0;  /* maybe, maybe ... */
2857 #    endif
2858 
2859     /* share display lists with normal layer context */
2860     togl->OverlayCtx = glXCreateContext(togl->display, visinfo, togl->Ctx,
2861             !togl->Indirect);
2862 
2863     swa.colormap = XCreateColormap(togl->display,
2864             XRootWindow(togl->display, visinfo->screen),
2865             visinfo->visual, AllocNone);
2866     togl->OverlayCmap = swa.colormap;
2867 
2868     swa.border_pixel = 0;
2869     swa.event_mask = ALL_EVENTS_MASK;
2870     togl->OverlayWindow = XCreateWindow(togl->display,
2871             Tk_WindowId(togl->TkWin), 0, 0,
2872             togl->Width, togl->Height, 0,
2873             visinfo->depth, InputOutput,
2874             visinfo->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);
2875 
2876     hPtr = Tcl_CreateHashEntry(&winPtr->dispPtr->winTable,
2877             (const char *) togl->OverlayWindow, &new_flag);
2878     Tcl_SetHashValue(hPtr, winPtr);
2879 
2880     /* XMapWindow(togl->display, togl->OverlayWindow); */
2881     togl->OverlayIsMapped = False;
2882 
2883     /* Make sure window manager installs our colormap */
2884     XSetWMColormapWindows(togl->display, togl->OverlayWindow,
2885             &togl->OverlayWindow, 1);
2886 
2887     return TCL_OK;
2888 
2889 #  elif defined(TOGL_WGL) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
2890     /* not yet implemented on these */
2891     return TCL_ERROR;
2892 #  endif
2893 }
2894 
2895 #endif /* TOGL_USE_OVERLAY */
2896 
2897 
2898 
2899 #ifdef TOGL_WGL
2900 #  define TOGL_CLASS_NAME "Togl Class"
2901 static Bool ToglClassInitialized = False;
2902 
2903 static LRESULT CALLBACK
Win32WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)2904 Win32WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2905 {
2906     LONG    result;
2907     Togl   *togl = (Togl *) GetWindowLongPtr(hwnd, 0);
2908     WNDCLASS childClass;
2909 
2910     switch (message) {
2911 
2912       case WM_WINDOWPOSCHANGED:
2913           /* Should be processed by DefWindowProc, otherwise a double buffered
2914            * context is not properly resized when the corresponding window is
2915            * resized. */
2916           break;
2917 
2918       case WM_DESTROY:
2919           if (togl && togl->TkWin != NULL) {
2920               if (togl->SetGrid > 0) {
2921                   Tk_UnsetGrid(togl->TkWin);
2922               }
2923               (void) Tcl_DeleteCommandFromToken(togl->Interp, togl->widgetCmd);
2924           }
2925           break;
2926 
2927       case WM_ERASEBKGND:
2928           /* We clear our own window */
2929           return 1;
2930 
2931       case WM_DISPLAYCHANGE:
2932           if (togl->PbufferFlag && hasARBPbuffer && !togl->pbufferLost) {
2933               queryPbuffer(togl->pbuf, WGL_PBUFFER_LOST_ARB,
2934                       &togl->pbufferLost);
2935           }
2936           /* FALLTHROUGH */
2937 
2938       default:
2939 #  if USE_STATIC_LIB
2940           return TkWinChildProc(hwnd, message, wParam, lParam);
2941 #  else
2942           /*
2943            * OK, since TkWinChildProc is not explicitly exported in the
2944            * dynamic libraries, we have to retrieve it from the class info
2945            * registered with windows.
2946            *
2947            */
2948           if (tkWinChildProc == NULL) {
2949               GetClassInfo(Tk_GetHINSTANCE(), TK_WIN_CHILD_CLASS_NAME,
2950                       &childClass);
2951               tkWinChildProc = childClass.lpfnWndProc;
2952           }
2953           return tkWinChildProc(hwnd, message, wParam, lParam);
2954 #  endif
2955     }
2956     result = DefWindowProc(hwnd, message, wParam, lParam);
2957     Tcl_ServiceAll();
2958     return result;
2959 }
2960 #endif /* TOGL_WGL */
2961 
2962 
2963 /*
2964  * Togl_MakeWindow
2965  *
2966  *   Window creation function, invoked as a callback from Tk_MakeWindowExist.
2967  *   This is called instead of TkpMakeWindow and must always succeed.
2968  */
2969 static Window
Togl_MakeWindow(Tk_Window tkwin,Window parent,ClientData instanceData)2970 Togl_MakeWindow(Tk_Window tkwin, Window parent, ClientData instanceData)
2971 {
2972     Togl   *togl = (Togl *) instanceData;
2973     Display *dpy;
2974     Colormap cmap;
2975     int     scrnum;
2976     Window  window = None;
2977 
2978 #if defined(TOGL_X11)
2979     Bool    directCtx = True;
2980     XSetWindowAttributes swa;
2981     int     width, height;
2982 #elif defined(TOGL_WGL)
2983     HWND    hwnd, parentWin;
2984     DWORD   style;
2985     HINSTANCE hInstance;
2986     PIXELFORMATDESCRIPTOR pfd;
2987     int     width, height;
2988     Bool    createdPbufferDC = False;
2989 #elif defined(TOGL_AGL)
2990 #endif
2991 
2992     if (togl->badWindow) {
2993         TkWindow *winPtr = (TkWindow *) tkwin;
2994 
2995         return TkpMakeWindow(winPtr, parent);
2996     }
2997 
2998     /* for color index mode photos */
2999     if (togl->RedMap)
3000         free(togl->RedMap);
3001     if (togl->GreenMap)
3002         free(togl->GreenMap);
3003     if (togl->BlueMap)
3004         free(togl->BlueMap);
3005     togl->RedMap = togl->GreenMap = togl->BlueMap = NULL;
3006     togl->MapSize = 0;
3007 
3008     dpy = Tk_Display(tkwin);
3009     scrnum = Tk_ScreenNumber(tkwin);
3010 
3011     /*
3012      * Windows and Mac OS X need the window created before OpenGL context
3013      * is created.  So do that now and set the window variable.
3014      */
3015 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3016     {
3017         TkWindow *winPtr = (TkWindow *) tkwin;
3018 
3019         window = TkpMakeWindow(winPtr, parent);
3020         if (!togl->PbufferFlag)
3021             (void) XMapWindow(dpy, window);
3022     }
3023 #elif defined(TOGL_WGL)
3024     hInstance = Tk_GetHINSTANCE();
3025     if (!ToglClassInitialized) {
3026         WNDCLASS ToglClass;
3027 
3028         ToglClassInitialized = True;
3029         ToglClass.style = CS_HREDRAW | CS_VREDRAW;
3030         ToglClass.cbClsExtra = 0;
3031         ToglClass.cbWndExtra = sizeof (LONG_PTR);       /* to save Togl* */
3032         ToglClass.hInstance = hInstance;
3033         ToglClass.hbrBackground = NULL;
3034         ToglClass.lpszMenuName = NULL;
3035         ToglClass.lpszClassName = TOGL_CLASS_NAME;
3036         ToglClass.lpfnWndProc = Win32WinProc;
3037         ToglClass.hIcon = NULL;
3038         ToglClass.hCursor = NULL;
3039         if (!RegisterClass(&ToglClass)) {
3040             Tcl_SetResult(togl->Interp,
3041                     TCL_STUPID "unable register Togl window class", TCL_STATIC);
3042             goto error;
3043         }
3044     }
3045 
3046     /* duplicate tkpMakeWindow logic from tk8.[45]/win/tkWinWindow.c */
3047     if (parent != None) {
3048         parentWin = Tk_GetHWND(parent);
3049         style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
3050     } else {
3051         parentWin = NULL;
3052         style = WS_POPUP | WS_CLIPCHILDREN;
3053     }
3054     if (togl->PbufferFlag) {
3055         width = height = 1;     /* TODO: demo code mishaves when set to 1000 */
3056     } else {
3057         width = togl->Width;
3058         height = togl->Height;
3059     }
3060     hwnd = CreateWindowEx(WS_EX_NOPARENTNOTIFY, TOGL_CLASS_NAME, NULL, style,
3061             0, 0, width, height, parentWin, NULL, hInstance, NULL);
3062     SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
3063             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
3064     window = Tk_AttachHWND(tkwin, hwnd);
3065     SetWindowLongPtr(hwnd, 0, (LONG_PTR) togl);
3066     if (togl->PbufferFlag) {
3067         ShowWindow(hwnd, SW_HIDE);      /* make sure it's hidden */
3068     }
3069 #endif
3070 
3071     /*
3072      * Figure out which OpenGL context to use
3073      */
3074 
3075 #ifdef TOGL_WGL
3076     togl->tglGLHdc = GetDC(hwnd);
3077 #endif
3078     if (togl->PixelFormat) {
3079 #if defined(TOGL_X11)
3080         XVisualInfo template;
3081         int     count = 0;
3082 
3083         template.visualid = togl->PixelFormat;
3084         togl->VisInfo = XGetVisualInfo(dpy, VisualIDMask, &template, &count);
3085         if (togl->VisInfo == NULL) {
3086             Tcl_SetResult(togl->Interp,
3087                     TCL_STUPID "missing visual information", TCL_STATIC);
3088             goto error;
3089         }
3090 #endif
3091         if (!togl_describePixelFormat(togl)) {
3092             Tcl_SetResult(togl->Interp,
3093                     TCL_STUPID "couldn't choose pixel format", TCL_STATIC);
3094             goto error;
3095         }
3096     } else {
3097 #if defined(TOGL_X11)
3098         togl->VisInfo = togl_pixelFormat(togl, scrnum);
3099         if (togl->VisInfo == NULL)
3100 #elif defined(TOGL_WGL) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3101 #  ifdef TOGL_WGL
3102         togl->PixelFormat = togl_pixelFormat(togl, hwnd);
3103 #  elif defined(TOGL_NSOPENGL)
3104         togl->PixelFormat = (void *)togl_pixelFormat(togl);
3105 #  else
3106         togl->PixelFormat = (Tcl_WideInt)togl_pixelFormat(togl);
3107 #  endif
3108         if (togl->PixelFormat == 0)
3109 #endif
3110         {
3111             goto error;
3112         }
3113     }
3114 #ifdef TOGL_WGL
3115     if (togl->PbufferFlag) {
3116         togl->pbuf = togl_createPbuffer(togl);
3117         if (togl->pbuf == NULL) {
3118             Tcl_SetResult(togl->Interp,
3119                     TCL_STUPID "couldn't create pbuffer", TCL_STATIC);
3120             goto error;
3121         }
3122         ReleaseDC(hwnd, togl->tglGLHdc);
3123         togl->tglGLHdc = getPbufferDC(togl->pbuf);
3124         createdPbufferDC = True;
3125     } else if (SetPixelFormat(togl->tglGLHdc, (int) togl->PixelFormat,
3126                     NULL) == FALSE) {
3127         Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't set pixel format",
3128                 TCL_STATIC);
3129         goto error;
3130     }
3131 #endif
3132 #if defined(TOGL_WGL) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3133     if (togl->VisInfo == NULL) {
3134         /*
3135          * Create a new OpenGL rendering context. And check to share lists.
3136          */
3137         Visual *visual;
3138 
3139         /* Just for portability, define the simplest visinfo */
3140         visual = DefaultVisual(dpy, scrnum);
3141         togl->VisInfo = (XVisualInfo *) calloc(1, sizeof (XVisualInfo));
3142         togl->VisInfo->screen = scrnum;
3143         togl->VisInfo->visual = visual;
3144         togl->VisInfo->visualid = visual->visualid;
3145 #  if defined(__cplusplus) || defined(c_plusplus)
3146         togl->VisInfo->c_class = visual->c_class;
3147 #  else
3148         togl->VisInfo->class = visual->class;
3149 #  endif
3150         togl->VisInfo->depth = visual->bits_per_rgb;
3151     }
3152 #endif
3153 
3154 #if defined(TOGL_X11)
3155     if (togl->Indirect) {
3156         directCtx = False;
3157     }
3158 #endif
3159 
3160     /*
3161      * Create a new OpenGL rendering context.
3162      */
3163 #if defined(TOGL_X11)
3164     if (togl->ShareList) {
3165         /* share display lists with existing togl widget */
3166         Togl   *shareWith = FindTogl(togl, togl->ShareList);
3167         GLXContext shareCtx;
3168         int     error_code;
3169 
3170         if (shareWith) {
3171             shareCtx = shareWith->Ctx;
3172             togl->contextTag = shareWith->contextTag;
3173         } else {
3174             shareCtx = None;
3175         }
3176         if (shareCtx) {
3177             togl_SetupXErrorHandler();
3178         }
3179         togl->Ctx = glXCreateContext(dpy, togl->VisInfo, shareCtx, directCtx);
3180         if (shareCtx && (error_code = togl_CheckForXError(togl))) {
3181             char    buf[256];
3182 
3183             togl->Ctx = NULL;
3184             XGetErrorText(dpy, error_code, buf, sizeof buf);
3185             Tcl_AppendResult(togl->Interp,
3186                     "unable to share display lists: ", buf, NULL);
3187             goto error;
3188         }
3189     } else {
3190         if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) {
3191             /* share OpenGL context with existing Togl widget */
3192             Togl   *shareWith = FindTogl(togl, togl->ShareContext);
3193 
3194             if (togl->VisInfo->visualid != shareWith->VisInfo->visualid) {
3195                 Tcl_SetResult(togl->Interp,
3196                         TCL_STUPID "unable to share OpenGL context",
3197                         TCL_STATIC);
3198                 goto error;
3199             }
3200             togl->Ctx = shareWith->Ctx;
3201         } else {
3202             /* don't share display lists */
3203             togl->ShareContext = False;
3204             togl->Ctx = glXCreateContext(dpy, togl->VisInfo, None, directCtx);
3205         }
3206     }
3207 #elif defined(TOGL_WGL)
3208     if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) {
3209         /* share OpenGL context with existing Togl widget */
3210         Togl   *shareWith = FindTogl(togl, togl->ShareContext);
3211 
3212         if (togl->PixelFormat != shareWith->PixelFormat) {
3213             Tcl_SetResult(togl->Interp,
3214                     TCL_STUPID "unable to share OpenGL context", TCL_STATIC);
3215             goto error;
3216         }
3217         togl->Ctx = shareWith->Ctx;
3218     } else {
3219         togl->Ctx = wglCreateContext(togl->tglGLHdc);
3220     }
3221 
3222     if (togl->ShareList) {
3223         /* share display lists with existing togl widget */
3224         Togl   *shareWith = FindTogl(togl, togl->ShareList);
3225 
3226         if (shareWith) {
3227             if (!wglShareLists(shareWith->Ctx, togl->Ctx)) {
3228 #  if 0
3229                 LPVOID  lpMsgBuf;
3230                 DWORD   err = GetLastError();
3231 
3232                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
3233                         FORMAT_MESSAGE_FROM_SYSTEM,
3234                         NULL, err,
3235                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
3236                         (LPTSTR) &lpMsgBuf, 0, NULL);
3237                 fprintf(stderr, "unable to share display lists: %d: %s\n",
3238                         err, lpMsgBuf);
3239                 LocalFree(lpMsgBuf);
3240 #  endif
3241                 Tcl_SetResult(togl->Interp,
3242                         TCL_STUPID "unable to share display lists", TCL_STATIC);
3243                 goto error;
3244             }
3245             togl->contextTag = shareWith->contextTag;
3246         }
3247     }
3248 #elif defined(TOGL_AGL)
3249     AGLContext shareCtx = NULL;
3250 
3251     if (togl->ShareList) {
3252         /* share display lists with existing togl widget */
3253         Togl   *shareWith = FindTogl(togl, togl->ShareList);
3254 
3255         if (shareWith) {
3256             shareCtx = shareWith->Ctx;
3257             togl->contextTag = shareWith->contextTag;
3258         }
3259     }
3260     if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) {
3261         /* share OpenGL context with existing Togl widget */
3262         Togl   *shareWith = FindTogl(togl, togl->ShareContext);
3263 
3264         if (togl->PixelFormat != shareWith->PixelFormat) {
3265             Tcl_SetResult(togl->Interp,
3266                     TCL_STUPID "unable to share OpenGL context", TCL_STATIC);
3267             goto error;
3268         }
3269         togl->Ctx = shareWith->Ctx;
3270     } else if ((togl->Ctx = aglCreateContext((AGLPixelFormat) togl->PixelFormat,
3271                             shareCtx)) == NULL) {
3272         GLenum  err = aglGetError();
3273 
3274         aglDestroyPixelFormat((AGLPixelFormat) togl->PixelFormat);
3275         togl->PixelFormat = 0;
3276         if (err == AGL_BAD_MATCH)
3277             Tcl_SetResult(togl->Interp,
3278                     TCL_STUPID "unable to share display lists"
3279                     ": shared context doesn't match", TCL_STATIC);
3280         else if (err == AGL_BAD_CONTEXT)
3281             Tcl_SetResult(togl->Interp,
3282                     TCL_STUPID "unable to share display lists"
3283                     ": bad shared context", TCL_STATIC);
3284         else if (err == AGL_BAD_PIXELFMT)
3285             Tcl_SetResult(togl->Interp,
3286                     TCL_STUPID "could not create rendering context"
3287                     ": bad pixel format", TCL_STATIC);
3288         else
3289             Tcl_SetResult(togl->Interp,
3290                     TCL_STUPID "could not create rendering context"
3291                     ": unknown reason", TCL_STATIC);
3292         goto error;
3293     }
3294 
3295     if (!togl->PbufferFlag
3296             && !aglSetDrawable(togl->Ctx, Togl_MacOSXGetDrawablePort(togl))) {
3297         /* aglSetDrawable is deprecated in OS X 10.5 */
3298         aglDestroyContext(togl->Ctx);
3299         togl->Ctx = NULL;
3300         aglDestroyPixelFormat((AGLPixelFormat) togl->PixelFormat);
3301         togl->PixelFormat = 0;
3302         Tcl_SetResult(togl->Interp,
3303                 TCL_STUPID "couldn't set drawable", TCL_STATIC);
3304         goto error;
3305     }
3306 #elif defined(TOGL_NSOPENGL)
3307     NSOpenGLContext *shareCtx = NULL;
3308     if (togl->ShareList) {
3309         /* share display lists with existing togl widget */
3310         Togl   *shareWith = FindTogl(togl, togl->ShareList);
3311 
3312         if (shareWith) {
3313             shareCtx = shareWith->Ctx;
3314             togl->contextTag = shareWith->contextTag;
3315         }
3316     }
3317     if (togl->ShareContext && FindTogl(togl, togl->ShareContext)) {
3318         /* share OpenGL context with existing Togl widget */
3319       Tcl_SetResult(togl->Interp,
3320                     TCL_STUPID "unable to share NSOpenGL context", TCL_STATIC);
3321       goto error;
3322       /*
3323         Togl   *shareWith = FindTogl(togl, togl->ShareContext);
3324 
3325         if (togl->PixelFormat != shareWith->PixelFormat) {
3326             Tcl_SetResult(togl->Interp,
3327                     TCL_STUPID "unable to share OpenGL context", TCL_STATIC);
3328             goto error;
3329         }
3330 	togl->Ctx = [[NSOpenGLContext alloc] initWithCGLContextObj:shareWith->Ctx];
3331       */
3332 	/* initWithCGLContextObj requires Mac OS 10.6 */
3333     } else {
3334       togl->Ctx = [NSOpenGLContext alloc];
3335       if ([togl->Ctx initWithFormat:togl->PixelFormat shareContext:shareCtx]
3336 	  == nil)
3337 	{
3338 	  [togl->PixelFormat release];
3339 	  togl->PixelFormat = 0;
3340 	  Tcl_SetResult(togl->Interp,
3341 			TCL_STUPID "Could not obtain OpenGL context",
3342 			TCL_STATIC);
3343 	  goto error;
3344 	}
3345     }
3346 
3347     if (!togl->PbufferFlag) {
3348       togl->nsview = [[ToglNSView alloc] initWithFrame:NSZeroRect];
3349       [togl->nsview setTogl:togl];
3350       if ([togl->nsview respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
3351 	[togl->nsview setWantsBestResolutionOpenGLSurface:YES];
3352       }
3353       [togl->nsview setAcceptsTouchEvents:YES];
3354       MacDrawable *d = ((TkWindow *) togl->TkWin)->privatePtr;
3355       NSView *topview = d->toplevel->view;
3356       [topview addSubview:togl->nsview];
3357       /* TODO: Appears setView has to be deferred until window mapped.
3358        * or it gives "invalid drawable" error.  But MapNotify doesn't happen.
3359        * I think toplevel is already mapped.  Iconifying and uniconifying
3360        * main window makes the graphics work.
3361        */
3362       /*      [togl->Ctx setView:togl->nsview];*/
3363     }
3364 #endif
3365 
3366     if (togl->Ctx == NULL) {
3367         Tcl_SetResult(togl->Interp,
3368                 TCL_STUPID "could not create rendering context", TCL_STATIC);
3369         goto error;
3370     }
3371 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3372     CGDisplayRegisterReconfigurationCallback(ReconfigureCB, togl);
3373 #endif
3374 
3375     if (togl->PbufferFlag) {
3376         /* Don't need a colormap, nor overlay, nor be displayed */
3377 #if defined(TOGL_X11) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3378         togl->pbuf = togl_createPbuffer(togl);
3379         if (!togl->pbuf) {
3380             /* tcl result set in togl_createPbuffer */
3381 #  ifdef TOGL_AGL
3382             if (!togl->ShareContext) {
3383                 aglDestroyContext(togl->Ctx);
3384                 aglDestroyPixelFormat((AGLPixelFormat) togl->PixelFormat);
3385             }
3386             togl->Ctx = NULL;
3387             togl->PixelFormat = 0;
3388 #  endif
3389 #  ifdef TOGL_NSOPENGL
3390             if (!togl->ShareContext) {
3391 	        [togl->Ctx release];
3392 		[togl->PixelFormat release];
3393             }
3394             togl->Ctx = NULL;
3395             togl->PixelFormat = 0;
3396 #  endif
3397             goto error;
3398         }
3399 #  ifdef TOGL_X11
3400         window = TkpMakeWindow((TkWindow *) tkwin, parent);
3401 #  endif
3402 #endif
3403         return window;
3404     }
3405 #ifdef TOGL_WGL
3406     DescribePixelFormat(togl->tglGLHdc, (int) togl->PixelFormat, sizeof (pfd),
3407             &pfd);
3408 #endif
3409 
3410     /*
3411      * find a colormap
3412      */
3413     if (togl->RgbaFlag) {
3414         /* Colormap for RGB mode */
3415 #if defined(TOGL_X11)
3416         cmap = get_rgb_colormap(dpy, scrnum, togl->VisInfo, tkwin);
3417 #elif defined(TOGL_WGL)
3418         if (pfd.dwFlags & PFD_NEED_PALETTE) {
3419             cmap = Win32CreateRgbColormap(pfd);
3420         } else {
3421             cmap = DefaultColormap(dpy, scrnum);
3422         }
3423 #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3424         cmap = DefaultColormap(dpy, scrnum);
3425 #endif
3426     } else {
3427         /* Colormap for CI mode */
3428 #ifdef TOGL_WGL
3429         /* this logic is to overcome a combination driver/compiler bug: (1)
3430          * cColorBits may be unusually large (e.g., 32 instead of 8 or 12) and
3431          * (2) 1 << 32 might be 1 instead of zero (gcc for ia32) */
3432         if (pfd.cColorBits >= MAX_CI_COLORMAP_BITS) {
3433             togl->CiColormapSize = MAX_CI_COLORMAP_SIZE;
3434         } else {
3435             togl->CiColormapSize = 1 << pfd.cColorBits;
3436             if (togl->CiColormapSize >= MAX_CI_COLORMAP_SIZE)
3437                 togl->CiColormapSize = MAX_CI_COLORMAP_SIZE;
3438         }
3439 
3440 #endif
3441         if (togl->PrivateCmapFlag) {
3442             /* need read/write colormap so user can store own color entries */
3443 #if defined(TOGL_X11)
3444             cmap = XCreateColormap(dpy, XRootWindow(dpy, togl->VisInfo->screen),
3445                     togl->VisInfo->visual, AllocAll);
3446 #elif defined(TOGL_WGL)
3447             cmap = Win32CreateCiColormap(togl);
3448 #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3449             /* need to figure out how to do this correctly on Mac... */
3450             cmap = DefaultColormap(dpy, scrnum);
3451 #endif
3452         } else {
3453             if (togl->VisInfo->visual == DefaultVisual(dpy, scrnum)) {
3454                 /* share default/root colormap */
3455                 cmap = Tk_Colormap(tkwin);
3456             } else {
3457                 /* make a new read-only colormap */
3458                 cmap = XCreateColormap(dpy,
3459                         XRootWindow(dpy, togl->VisInfo->screen),
3460                         togl->VisInfo->visual, AllocNone);
3461             }
3462         }
3463     }
3464 
3465     /* Make sure Tk knows to switch to the new colormap when the cursor is over
3466      * this window when running in color index mode. */
3467     (void) Tk_SetWindowVisual(tkwin, togl->VisInfo->visual,
3468             togl->VisInfo->depth, cmap);
3469 
3470 #ifdef TOGL_WGL
3471     /* Install the colormap */
3472     SelectPalette(togl->tglGLHdc, ((TkWinColormap *) cmap)->palette, TRUE);
3473     RealizePalette(togl->tglGLHdc);
3474 #endif
3475 
3476 #if defined(TOGL_X11)
3477     swa.background_pixmap = None;
3478     swa.border_pixel = 0;
3479     swa.colormap = cmap;
3480     swa.event_mask = ALL_EVENTS_MASK;
3481     if (togl->PbufferFlag) {
3482         width = height = 1;
3483     } else {
3484         width = togl->Width;
3485         height = togl->Height;
3486     }
3487     window = XCreateWindow(dpy, parent,
3488             0, 0, width, height,
3489             0, togl->VisInfo->depth, InputOutput, togl->VisInfo->visual,
3490             CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask, &swa);
3491     /* Make sure window manager installs our colormap */
3492     (void) XSetWMColormapWindows(dpy, window, &window, 1);
3493 
3494     if (!togl->DoubleFlag) {
3495         int     dbl_flag;
3496 
3497         /* See if we requested single buffering but had to accept a double
3498          * buffered visual.  If so, set the GL draw buffer to be the front
3499          * buffer to simulate single buffering. */
3500         if (glXGetConfig(dpy, togl->VisInfo, GLX_DOUBLEBUFFER, &dbl_flag)) {
3501             if (dbl_flag) {
3502                 glXMakeCurrent(dpy, window, togl->Ctx);
3503                 glDrawBuffer(GL_FRONT);
3504                 glReadBuffer(GL_FRONT);
3505             }
3506         }
3507     }
3508 #elif defined(TOGL_WGL)
3509     if (!togl->DoubleFlag) {
3510         /* See if we requested single buffering but had to accept a double
3511          * buffered visual.  If so, set the GL draw buffer to be the front
3512          * buffer to simulate single buffering. */
3513         if (getPixelFormatAttribiv == NULL) {
3514             /* pfd is already set */
3515             if ((pfd.dwFlags & PFD_DOUBLEBUFFER) != 0) {
3516                 wglMakeCurrent(togl->tglGLHdc, togl->Ctx);
3517                 glDrawBuffer(GL_FRONT);
3518                 glReadBuffer(GL_FRONT);
3519             }
3520         } else {
3521             static int attribs[] = {
3522                 WGL_DOUBLE_BUFFER_ARB,
3523             };
3524 #  define NUM_ATTRIBS (sizeof attribs / sizeof attribs[0])
3525             int     info[NUM_ATTRIBS];
3526 
3527             getPixelFormatAttribiv(togl->tglGLHdc, (int) togl->PixelFormat, 0,
3528                     NUM_ATTRIBS, attribs, info);
3529 #  undef NUM_ATTRIBS
3530             if (info[0]) {
3531                 wglMakeCurrent(togl->tglGLHdc, togl->Ctx);
3532                 glDrawBuffer(GL_FRONT);
3533                 glReadBuffer(GL_FRONT);
3534             }
3535         }
3536     }
3537 #endif
3538 
3539 #if TOGL_USE_OVERLAY
3540     if (togl->OverlayFlag) {
3541         if (SetupOverlay(togl) == TCL_ERROR) {
3542             fprintf(stderr, "Warning: couldn't setup overlay.\n");
3543             togl->OverlayFlag = False;
3544         }
3545     }
3546 #endif
3547 
3548 #if !defined(TOGL_AGL)
3549     /* Request the X window to be displayed */
3550     (void) XMapWindow(dpy, window);
3551 #endif
3552 
3553     if (!togl->RgbaFlag) {
3554         int     index_size;
3555 
3556 #if defined(TOGL_X11) || defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3557         GLint   index_bits;
3558 
3559         glGetIntegerv(GL_INDEX_BITS, &index_bits);
3560         index_size = 1 << index_bits;
3561 #elif defined(TOGL_WGL)
3562         index_size = togl->CiColormapSize;
3563 #endif
3564         if (togl->MapSize != index_size) {
3565             if (togl->RedMap)
3566                 free(togl->RedMap);
3567             if (togl->GreenMap)
3568                 free(togl->GreenMap);
3569             if (togl->BlueMap)
3570                 free(togl->BlueMap);
3571             togl->MapSize = index_size;
3572             togl->RedMap = (GLfloat *) calloc(index_size, sizeof (GLfloat));
3573             togl->GreenMap = (GLfloat *) calloc(index_size, sizeof (GLfloat));
3574             togl->BlueMap = (GLfloat *) calloc(index_size, sizeof (GLfloat));
3575         }
3576     }
3577 #ifdef HAVE_AUTOSTEREO
3578     if (togl->Stereo == TOGL_STEREO_NATIVE) {
3579         if (!togl->as_initialized) {
3580             const char *autostereod;
3581 
3582             togl->as_initialized = True;
3583             if ((autostereod = getenv("AUTOSTEREOD")) == NULL)
3584                 autostereod = AUTOSTEREOD;
3585             if (autostereod && *autostereod) {
3586                 if (ASInitialize(togl->display, autostereod) == Success) {
3587                     togl->ash = ASCreatedStereoWindow(dpy);
3588                 }
3589             }
3590         } else {
3591             togl->ash = ASCreatedStereoWindow(dpy);
3592         }
3593     }
3594 #endif
3595 
3596     return window;
3597 
3598   error:
3599 
3600     togl->badWindow = True;
3601 
3602 #if defined(TOGL_X11)
3603     if (window == None) {
3604         TkWindow *winPtr = (TkWindow *) tkwin;
3605 
3606         window = TkpMakeWindow(winPtr, parent);
3607     }
3608 #elif defined(TOGL_WGL)
3609     if (togl->tglGLHdc) {
3610         if (createdPbufferDC)
3611             releasePbufferDC(togl->pbuf, togl->tglGLHdc);
3612         else
3613             ReleaseDC(hwnd, togl->tglGLHdc);
3614         togl->tglGLHdc = NULL;
3615     }
3616 #endif
3617     return window;
3618 }
3619 
3620 /*
3621  * Togl_WorldChanged
3622  *
3623  *    Add support for setgrid option.
3624  */
3625 static void
Togl_WorldChanged(ClientData instanceData)3626 Togl_WorldChanged(ClientData instanceData)
3627 {
3628     Togl   *togl = (Togl *) instanceData;
3629     int     width;
3630     int     height;
3631 
3632     if (togl->PbufferFlag)
3633         width = height = 1;
3634     else {
3635         width = togl->Width;
3636         height = togl->Height;
3637     }
3638     Tk_GeometryRequest(togl->TkWin, width, height);
3639     Tk_SetInternalBorder(togl->TkWin, 0);
3640     if (togl->SetGrid > 0) {
3641         Tk_SetGrid(togl->TkWin, width / togl->SetGrid,
3642                 height / togl->SetGrid, togl->SetGrid, togl->SetGrid);
3643     } else {
3644         Tk_UnsetGrid(togl->TkWin);
3645     }
3646 }
3647 
3648 static void
Togl_SetViewPort(const struct Togl * togl)3649 Togl_SetViewPort(const struct Togl *togl)
3650 {
3651   glViewport(0, 0, togl->Width*togl->PixelScale, togl->Height*togl->PixelScale);
3652 }
3653 
3654 /*
3655  * ToglFree
3656  *
3657  *      Wrap the ckfree macro.
3658  */
3659 static void
ToglFree(char * clientData)3660 ToglFree(char *clientData)
3661 {
3662     ckfree(clientData);
3663 }
3664 
3665 /*
3666  * ToglCmdDeletedProc
3667  *
3668  *      This procedure is invoked when a widget command is deleted.  If
3669  *      the widget isn't already in the process of being destroyed,
3670  *      this command destroys it.
3671  *
3672  * Results:
3673  *      None.
3674  *
3675  * Side effects:
3676  *      The widget is destroyed.
3677  *
3678  *----------------------------------------------------------------------
3679  */
3680 static void
ToglCmdDeletedProc(ClientData clientData)3681 ToglCmdDeletedProc(ClientData clientData)
3682 {
3683     Togl   *togl = (Togl *) clientData;
3684     Tk_Window tkwin = togl->TkWin;
3685 
3686     /*
3687      * This procedure could be invoked either because the window was
3688      * destroyed and the command was then deleted (in which case tkwin
3689      * is NULL) or because the command was deleted, and then this procedure
3690      * destroys the widget.
3691      */
3692 
3693     if (tkwin) {
3694         Tk_DeleteEventHandler(tkwin, ExposureMask | StructureNotifyMask,
3695                 Togl_EventProc, (ClientData) togl);
3696     }
3697 
3698     Tk_Preserve((ClientData) togl);
3699     Tcl_EventuallyFree((ClientData) togl, ToglFree);
3700 
3701     Togl_LeaveStereo(togl, togl->Stereo);
3702 
3703     if (togl->DestroyProc) {
3704         /* call user's cleanup code */
3705         Togl_CallCallback(togl, togl->DestroyProc);
3706     }
3707 
3708     if (togl->TimerProc != NULL) {
3709         Tcl_DeleteTimerHandler(togl->timerHandler);
3710         togl->timerHandler = NULL;
3711     }
3712     if (togl->UpdatePending) {
3713         Tcl_CancelIdleCall(Togl_Render, (ClientData) togl);
3714         togl->UpdatePending = False;
3715     }
3716 #ifndef NO_TK_CURSOR
3717     if (togl->Cursor != None) {
3718         Tk_FreeCursor(togl->display, togl->Cursor);
3719         togl->Cursor = None;
3720     }
3721 #endif
3722 
3723     /* remove from linked list */
3724     RemoveFromList(togl);
3725 
3726     togl->TkWin = NULL;
3727     if (tkwin != NULL) {
3728 
3729         if (togl->Ctx) {
3730             if (FindToglWithSameContext(togl) == NULL) {
3731 #if defined(TOGL_X11)
3732                 glXDestroyContext(togl->display, togl->Ctx);
3733 #elif defined(TOGL_WGL)
3734                 wglDeleteContext(togl->Ctx);
3735 #elif defined(TOGL_AGL)
3736                 aglDestroyContext(togl->Ctx);
3737                 CGDisplayRemoveReconfigurationCallback(ReconfigureCB, togl);
3738 #elif defined(TOGL_NSOPENGL)
3739 		[togl->Ctx release];
3740 		togl->Ctx = nil;
3741 		[togl->nsview setTogl:nil];
3742 		[togl->nsview release];
3743 		togl->nsview = nil;
3744                 CGDisplayRemoveReconfigurationCallback(ReconfigureCB, togl);
3745 #endif
3746 #if defined(TOGL_X11)
3747                 XFree(togl->VisInfo);
3748 #else
3749                 free(togl->VisInfo);
3750 #endif
3751             }
3752 #if defined(TOGL_WGL)
3753             if (togl->tglGLHdc) {
3754                 if (togl->PbufferFlag) {
3755                     releasePbufferDC(togl->pbuf, togl->tglGLHdc);
3756                 } else {
3757                     HWND    hwnd = Tk_GetHWND(Tk_WindowId(tkwin));
3758 
3759                     ReleaseDC(hwnd, togl->tglGLHdc);
3760                 }
3761                 togl->tglGLHdc = NULL;
3762             }
3763 #endif
3764             if (togl->PbufferFlag && togl->pbuf) {
3765                 togl_destroyPbuffer(togl);
3766                 togl->pbuf = 0;
3767             }
3768             togl->Ctx = NULL;
3769             togl->VisInfo = NULL;
3770         }
3771 #if defined(TOGL_X11)
3772 #  if TOGL_USE_OVERLAY
3773         if (togl->OverlayCtx) {
3774             Tcl_HashEntry *entryPtr;
3775             TkWindow *winPtr = (TkWindow *) tkwin;
3776 
3777             if (winPtr) {
3778                 entryPtr = Tcl_FindHashEntry(&winPtr->dispPtr->winTable,
3779                         (const char *) togl->OverlayWindow);
3780                 Tcl_DeleteHashEntry(entryPtr);
3781             }
3782             if (FindToglWithSameOverlayContext(togl) == NULL)
3783                 glXDestroyContext(togl->display, togl->OverlayCtx);
3784             togl->OverlayCtx = NULL;
3785         }
3786 #  endif /* TOGL_USE_OVERLAY */
3787 #endif
3788 
3789         if (togl->SetGrid > 0) {
3790             Tk_UnsetGrid(tkwin);
3791         }
3792         Tk_DestroyWindow(tkwin);
3793     }
3794 
3795     Tk_Release((ClientData) togl);
3796 }
3797 
3798 
3799 /*
3800  * This gets called to track top level position changes for
3801  * row interleaved stereo.
3802  */
3803 static void
Togl_RedisplayProc(ClientData clientData,XEvent * eventPtr)3804 Togl_RedisplayProc(ClientData clientData, XEvent *eventPtr)
3805 {
3806     Togl   *togl = (Togl *) clientData;
3807 
3808     switch (eventPtr->type) {
3809       case ConfigureNotify:
3810           Togl_PostRedisplay(togl);
3811           break;
3812     }
3813 }
3814 
3815 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3816 static int
viewPixelScale(NSView * nsview)3817 viewPixelScale(NSView *nsview)
3818 {
3819   int pixelScale = 1;
3820   if ([nsview respondsToSelector:@selector(convertRectToBacking:)])
3821     {
3822       NSRect wbounds = [nsview bounds];
3823       NSRect gbounds = [nsview convertRectToBacking:wbounds];
3824       pixelScale = (wbounds.size.width > 0 ?
3825 		    gbounds.size.width / wbounds.size.width : 1);
3826     }
3827   return pixelScale;
3828 }
3829 #endif
3830 
3831 /*
3832  * This gets called to handle Togl window configuration events
3833  */
3834 static void
Togl_EventProc(ClientData clientData,XEvent * eventPtr)3835 Togl_EventProc(ClientData clientData, XEvent *eventPtr)
3836 {
3837     Togl   *togl = (Togl *) clientData;
3838 
3839     switch (eventPtr->type) {
3840       case Expose:
3841 #if defined(TOGL_NSOPENGL)
3842           if (!Tk_IsMapped(togl->TkWin))
3843               /* Tk Cocoa generates expose events for unmapped windows! */
3844               break;
3845 #endif
3846           if (eventPtr->xexpose.count == 0) {
3847               if (!togl->UpdatePending
3848                       && eventPtr->xexpose.window == Tk_WindowId(togl->TkWin)) {
3849                   Togl_PostRedisplay(togl);
3850               }
3851 #if defined(TOGL_X11)
3852               if (!togl->OverlayUpdatePending && togl->OverlayFlag
3853                       && togl->OverlayIsMapped
3854                       && eventPtr->xexpose.window == togl->OverlayWindow) {
3855                   Togl_PostOverlayRedisplay(togl);
3856               }
3857 #endif
3858 #if defined(TOGL_NSOPENGL)
3859               [togl->Ctx setView:togl->nsview];
3860               SetMacBufRect(togl);
3861 #endif
3862           }
3863           break;
3864       case ConfigureNotify:
3865           if (togl->PbufferFlag)
3866               break;
3867 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3868 	  int pixelScale = viewPixelScale(togl->nsview);
3869           if (togl->Width == Tk_Width(togl->TkWin)
3870 	      && togl->Height == Tk_Height(togl->TkWin)
3871 	      && togl->PixelScale == pixelScale) {
3872 
3873               // Even though the size hasn't changed,
3874               // it's position on the screen may have.
3875               if (Tk_IsMapped(togl->TkWin))
3876                   SetMacBufRect(togl);
3877               break;
3878           }
3879 #endif
3880           togl->Width = Tk_Width(togl->TkWin);
3881           togl->Height = Tk_Height(togl->TkWin);
3882 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3883 	  togl->PixelScale = pixelScale;
3884 #endif
3885           (void) XResizeWindow(Tk_Display(togl->TkWin),
3886                   Tk_WindowId(togl->TkWin), togl->Width, togl->Height);
3887 #if defined(TOGL_X11)
3888           if (togl->OverlayFlag) {
3889               (void) XResizeWindow(Tk_Display(togl->TkWin),
3890                       togl->OverlayWindow, togl->Width, togl->Height);
3891               (void) XRaiseWindow(Tk_Display(togl->TkWin), togl->OverlayWindow);
3892           }
3893 #endif
3894 
3895 #if defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
3896           SetMacBufRect(togl);
3897 #endif
3898 
3899           Togl_MakeCurrent(togl);
3900           if (togl->ReshapeProc) {
3901               Togl_SetViewPort(togl);
3902               (void) Togl_CallCallback(togl, togl->ReshapeProc);
3903           } else {
3904               Togl_SetViewPort(togl);
3905 #if defined(TOGL_X11)
3906               if (togl->OverlayFlag) {
3907                   Togl_UseLayer(togl, TOGL_OVERLAY);
3908                   Togl_SetViewPort(togl);
3909                   Togl_UseLayer(togl, TOGL_NORMAL);
3910               }
3911 #endif
3912           }
3913           break;
3914       case MapNotify:
3915 #if defined(TOGL_AGL)
3916           if (!togl->PbufferFlag) {
3917               /*
3918                * See comment for the UnmapNotify case below.
3919                */
3920               AGLDrawable d = Togl_MacOSXGetDrawablePort(togl);
3921 
3922               /* aglSetDrawable is deprecated in OS X 10.5 */
3923               aglSetDrawable(togl->Ctx, d);
3924               SetMacBufRect(togl);
3925           }
3926 #endif
3927 #if defined(TOGL_NSOPENGL)
3928           if (!togl->PbufferFlag) {
3929               /*
3930                * See comment for the UnmapNotify case below.
3931                */
3932 	      [togl->Ctx setView:togl->nsview];
3933               SetMacBufRect(togl);
3934           }
3935 #endif
3936           break;
3937       case UnmapNotify:
3938 #if defined(TOGL_AGL)
3939           if (!togl->PbufferFlag) {
3940               /*
3941                * For Mac OS X Aqua, Tk subwindows are not implemented as
3942                * separate Aqua windows.  They are just different regions of
3943                * a single Aqua window.  To unmap them they are just not drawn.
3944                * Have to disconnect the AGL context otherwise they will continue
3945                * to be displayed directly by Aqua.
3946                */
3947               /* aglSetDrawable is deprecated in OS X 10.5 */
3948               aglSetDrawable(togl->Ctx, NULL);
3949           }
3950 #endif
3951 #if defined(TOGL_NSOPENGL)
3952           if (!togl->PbufferFlag) {
3953               /*
3954                * For Mac OS X Aqua, Tk subwindows are not implemented as
3955                * separate Aqua windows.  They are just different regions of
3956                * a single Aqua window.  To unmap them they are just not drawn.
3957                * Have to disconnect the NSView otherwise they will continue
3958                * to be displayed directly by Aqua.
3959                */
3960               [togl->Ctx clearDrawable];
3961           }
3962 #endif
3963           break;
3964       case DestroyNotify:
3965           if (togl->TkWin != NULL) {
3966 #ifdef TOGL_WGL
3967               HWND    hwnd = Tk_GetHWND(Tk_WindowId(togl->TkWin));
3968 
3969               /* Prevent Win32WinProc from calling Tcl_DeleteCommandFromToken
3970                * a second time */
3971               SetWindowLongPtr(hwnd, 0, (LONG_PTR) 0);
3972 #endif
3973               if (togl->SetGrid > 0) {
3974                   Tk_UnsetGrid(togl->TkWin);
3975               }
3976               (void) Tcl_DeleteCommandFromToken(togl->Interp, togl->widgetCmd);
3977           }
3978           break;
3979       default:
3980           /* nothing */
3981           ;
3982     }
3983 }
3984 
3985 
3986 void
Togl_PostRedisplay(Togl * togl)3987 Togl_PostRedisplay(Togl *togl)
3988 {
3989     if (!togl->UpdatePending) {
3990         togl->UpdatePending = True;
3991         Tk_DoWhenIdle(Togl_Render, (ClientData) togl);
3992     }
3993 }
3994 
3995 
3996 Bool
Togl_UpdatePending(const Togl * togl)3997 Togl_UpdatePending(const Togl *togl)
3998 {
3999     return togl->UpdatePending;
4000 }
4001 
4002 
4003 void
Togl_SwapBuffers(const Togl * togl)4004 Togl_SwapBuffers(const Togl *togl)
4005 {
4006     if (togl->DoubleFlag) {
4007 #if defined(TOGL_WGL)
4008         int     res = SwapBuffers(togl->tglGLHdc);
4009 
4010         if (!res) {
4011             ErrorExit(TEXT("SwapBuffers"));
4012         }
4013 #elif defined(TOGL_X11)
4014         glXSwapBuffers(Tk_Display(togl->TkWin), Tk_WindowId(togl->TkWin));
4015 #elif defined(TOGL_AGL)
4016         aglSwapBuffers(togl->Ctx);
4017 #elif defined(TOGL_NSOPENGL)
4018         [togl->Ctx flushBuffer];
4019 #endif
4020     } else {
4021         glFlush();
4022     }
4023 }
4024 
4025 
4026 
4027 const char *
Togl_Ident(const Togl * togl)4028 Togl_Ident(const Togl *togl)
4029 {
4030     return togl->Ident;
4031 }
4032 
4033 
4034 int
Togl_Width(const Togl * togl)4035 Togl_Width(const Togl *togl)
4036 {
4037     return togl->Width;
4038 }
4039 
4040 
4041 int
Togl_Height(const Togl * togl)4042 Togl_Height(const Togl *togl)
4043 {
4044     return togl->Height;
4045 }
4046 
4047 int
Togl_PixelScale(const Togl * togl)4048 Togl_PixelScale(const Togl *togl)
4049 {
4050     return togl->PixelScale;
4051 }
4052 
4053 
4054 Tcl_Interp *
Togl_Interp(const Togl * togl)4055 Togl_Interp(const Togl *togl)
4056 {
4057     return togl->Interp;
4058 }
4059 
4060 
4061 Tk_Window
Togl_TkWin(const Togl * togl)4062 Togl_TkWin(const Togl *togl)
4063 {
4064     return togl->TkWin;
4065 }
4066 
4067 
4068 const char *
Togl_CommandName(const Togl * togl)4069 Togl_CommandName(const Togl *togl)
4070 {
4071     return Tcl_GetCommandName(togl->Interp, togl->widgetCmd);
4072 }
4073 
4074 int
Togl_ContextTag(const Togl * togl)4075 Togl_ContextTag(const Togl *togl)
4076 {
4077     return togl->contextTag;
4078 }
4079 
4080 Bool
Togl_HasRGBA(const Togl * togl)4081 Togl_HasRGBA(const Togl *togl)
4082 {
4083     return togl->RgbaFlag;
4084 }
4085 
4086 Bool
Togl_IsDoubleBuffered(const Togl * togl)4087 Togl_IsDoubleBuffered(const Togl *togl)
4088 {
4089     return togl->DoubleFlag;
4090 }
4091 
4092 Bool
Togl_HasDepthBuffer(const Togl * togl)4093 Togl_HasDepthBuffer(const Togl *togl)
4094 {
4095     return togl->DepthFlag;
4096 }
4097 
4098 Bool
Togl_HasAccumulationBuffer(const Togl * togl)4099 Togl_HasAccumulationBuffer(const Togl *togl)
4100 {
4101     return togl->AccumFlag;
4102 }
4103 
4104 Bool
Togl_HasDestinationAlpha(const Togl * togl)4105 Togl_HasDestinationAlpha(const Togl *togl)
4106 {
4107     return togl->AlphaFlag;
4108 }
4109 
4110 Bool
Togl_HasStencilBuffer(const Togl * togl)4111 Togl_HasStencilBuffer(const Togl *togl)
4112 {
4113     return togl->StencilFlag;
4114 }
4115 
4116 int
Togl_StereoMode(const Togl * togl)4117 Togl_StereoMode(const Togl *togl)
4118 {
4119     return togl->Stereo;
4120 }
4121 
4122 Bool
Togl_HasMultisample(const Togl * togl)4123 Togl_HasMultisample(const Togl *togl)
4124 {
4125     return togl->MultisampleFlag;
4126 }
4127 
4128 
4129 #if defined(TOGL_X11)
4130 /*
4131  * A replacement for XAllocColor.  This function should never
4132  * fail to allocate a color.  When XAllocColor fails, we return
4133  * the nearest matching color.  If we have to allocate many colors
4134  * this function isn't too efficient; the XQueryColors() could be
4135  * done just once.
4136  * Written by Michael Pichler, Brian Paul, Mark Kilgard
4137  * Input:  dpy - X display
4138  *         cmap - X colormap
4139  *         cmapSize - size of colormap
4140  * In/Out: color - the XColor struct
4141  * Output:  exact - 1=exact color match, 0=closest match
4142  */
4143 static void
noFaultXAllocColor(Display * dpy,Colormap cmap,int cmapSize,XColor * color,int * exact)4144 noFaultXAllocColor(Display *dpy, Colormap cmap, int cmapSize,
4145         XColor *color, int *exact)
4146 {
4147     XColor *ctable, subColor;
4148     int     i, bestmatch;
4149     double  mindist;            /* 3*2^16^2 exceeds long int precision. */
4150 
4151     /* First try just using XAllocColor. */
4152     if (XAllocColor(dpy, cmap, color)) {
4153         *exact = 1;
4154         return;
4155     }
4156 
4157     /* Retrieve color table entries. */
4158     /* XXX alloca candidate. */
4159     ctable = (XColor *) ckalloc(cmapSize * sizeof (XColor));
4160     for (i = 0; i < cmapSize; i++) {
4161         ctable[i].pixel = i;
4162     }
4163     (void) XQueryColors(dpy, cmap, ctable, cmapSize);
4164 
4165     /* Find best match. */
4166     bestmatch = -1;
4167     mindist = 0;
4168     for (i = 0; i < cmapSize; i++) {
4169         double  dr = (double) color->red - (double) ctable[i].red;
4170         double  dg = (double) color->green - (double) ctable[i].green;
4171         double  db = (double) color->blue - (double) ctable[i].blue;
4172         double  dist = dr * dr + dg * dg + db * db;
4173 
4174         if (bestmatch < 0 || dist < mindist) {
4175             bestmatch = i;
4176             mindist = dist;
4177         }
4178     }
4179 
4180     /* Return result. */
4181     subColor.red = ctable[bestmatch].red;
4182     subColor.green = ctable[bestmatch].green;
4183     subColor.blue = ctable[bestmatch].blue;
4184     ckfree((char *) ctable);
4185     /* Try to allocate the closest match color.  This should only fail if the
4186      * cell is read/write.  Otherwise, we're incrementing the cell's reference
4187      * count. */
4188     if (!XAllocColor(dpy, cmap, &subColor)) {
4189         /* do this to work around a problem reported by Frank Ortega */
4190         subColor.pixel = (unsigned long) bestmatch;
4191         subColor.red = ctable[bestmatch].red;
4192         subColor.green = ctable[bestmatch].green;
4193         subColor.blue = ctable[bestmatch].blue;
4194         subColor.flags = DoRed | DoGreen | DoBlue;
4195     }
4196     *color = subColor;
4197 }
4198 
4199 #elif defined(TOGL_WGL)
4200 
4201 static UINT
Win32AllocColor(const Togl * togl,float red,float green,float blue)4202 Win32AllocColor(const Togl *togl, float red, float green, float blue)
4203 {
4204     /* Modified version of XAllocColor emulation of Tk. - returns index,
4205      * instead of color itself - allocates logical palette entry even for
4206      * non-palette devices */
4207 
4208     TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin);
4209     UINT    index;
4210     COLORREF newColor, closeColor;
4211     PALETTEENTRY entry, closeEntry;
4212     int     isNew, refCount;
4213     Tcl_HashEntry *entryPtr;
4214 
4215     entry.peRed = (unsigned char) (red * 255 + .5);
4216     entry.peGreen = (unsigned char) (green * 255 + .5);
4217     entry.peBlue = (unsigned char) (blue * 255 + .5);
4218     entry.peFlags = 0;
4219 
4220     /*
4221      * Find the nearest existing palette entry.
4222      */
4223 
4224     newColor = RGB(entry.peRed, entry.peGreen, entry.peBlue);
4225     index = GetNearestPaletteIndex(cmap->palette, newColor);
4226     GetPaletteEntries(cmap->palette, index, 1, &closeEntry);
4227     closeColor = RGB(closeEntry.peRed, closeEntry.peGreen, closeEntry.peBlue);
4228 
4229     /*
4230      * If this is not a duplicate and colormap is not full, allocate a new entry.
4231      */
4232 
4233     if (newColor != closeColor) {
4234         if (cmap->size == (unsigned int) togl->CiColormapSize) {
4235             entry = closeEntry;
4236         } else {
4237             cmap->size++;
4238             ResizePalette(cmap->palette, cmap->size);
4239             index = cmap->size - 1;
4240             SetPaletteEntries(cmap->palette, index, 1, &entry);
4241             SelectPalette(togl->tglGLHdc, cmap->palette, TRUE);
4242             RealizePalette(togl->tglGLHdc);
4243         }
4244     }
4245     newColor = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue);
4246     entryPtr = Tcl_CreateHashEntry(&cmap->refCounts,
4247             (CONST char *) newColor, &isNew);
4248     if (isNew) {
4249         refCount = 1;
4250     } else {
4251         refCount = ((int) Tcl_GetHashValue(entryPtr)) + 1;
4252     }
4253     Tcl_SetHashValue(entryPtr, (ClientData) refCount);
4254 
4255     /* for color index mode photos */
4256     togl->RedMap[index] = (GLfloat) (entry.peRed / 255.0);
4257     togl->GreenMap[index] = (GLfloat) (entry.peGreen / 255.0);
4258     togl->BlueMap[index] = (GLfloat) (entry.peBlue / 255.0);
4259     return index;
4260 }
4261 
4262 static void
Win32FreeColor(const Togl * togl,unsigned long index)4263 Win32FreeColor(const Togl *togl, unsigned long index)
4264 {
4265     TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin);
4266     COLORREF cref;
4267     UINT    count, refCount;
4268     PALETTEENTRY entry, *entries;
4269     Tcl_HashEntry *entryPtr;
4270 
4271     if (index >= cmap->size) {
4272         panic("Tried to free a color that isn't allocated.");
4273     }
4274     GetPaletteEntries(cmap->palette, index, 1, &entry);
4275 
4276     cref = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue);
4277     entryPtr = Tcl_FindHashEntry(&cmap->refCounts, (CONST char *) cref);
4278     if (!entryPtr) {
4279         panic("Tried to free a color that isn't allocated.");
4280     }
4281     refCount = (int) Tcl_GetHashValue(entryPtr) - 1;
4282     if (refCount == 0) {
4283         count = cmap->size - index;
4284         entries = (PALETTEENTRY *) ckalloc(sizeof (PALETTEENTRY) * count);
4285         GetPaletteEntries(cmap->palette, index + 1, count, entries);
4286         SetPaletteEntries(cmap->palette, index, count, entries);
4287         SelectPalette(togl->tglGLHdc, cmap->palette, TRUE);
4288         RealizePalette(togl->tglGLHdc);
4289         ckfree((char *) entries);
4290         cmap->size--;
4291         Tcl_DeleteHashEntry(entryPtr);
4292     } else {
4293         Tcl_SetHashValue(entryPtr, (ClientData) refCount);
4294     }
4295 }
4296 
4297 static void
Win32SetColor(const Togl * togl,unsigned long index,float red,float green,float blue)4298 Win32SetColor(const Togl *togl,
4299         unsigned long index, float red, float green, float blue)
4300 {
4301     TkWinColormap *cmap = (TkWinColormap *) Tk_Colormap(togl->TkWin);
4302     PALETTEENTRY entry;
4303 
4304     entry.peRed = (unsigned char) (red * 255 + .5);
4305     entry.peGreen = (unsigned char) (green * 255 + .5);
4306     entry.peBlue = (unsigned char) (blue * 255 + .5);
4307     entry.peFlags = 0;
4308     SetPaletteEntries(cmap->palette, index, 1, &entry);
4309     SelectPalette(togl->tglGLHdc, cmap->palette, TRUE);
4310     RealizePalette(togl->tglGLHdc);
4311 
4312     /* for color index mode photos */
4313     togl->RedMap[index] = (GLfloat) (entry.peRed / 255.0);
4314     togl->GreenMap[index] = (GLfloat) (entry.peGreen / 255.0);
4315     togl->BlueMap[index] = (GLfloat) (entry.peBlue / 255.0);
4316 }
4317 #endif /* TOGL_X11 */
4318 
4319 
4320 unsigned long
Togl_AllocColor(const Togl * togl,float red,float green,float blue)4321 Togl_AllocColor(const Togl *togl, float red, float green, float blue)
4322 {
4323     if (togl->RgbaFlag) {
4324         (void) fprintf(stderr,
4325                 "Error: Togl_AllocColor illegal in RGBA mode.\n");
4326         return 0;
4327     }
4328     /* TODO: maybe not... */
4329     if (togl->PrivateCmapFlag) {
4330         (void) fprintf(stderr,
4331                 "Error: Togl_AllocColor illegal with private colormap\n");
4332         return 0;
4333     }
4334 #if defined(TOGL_X11)
4335     {
4336         XColor  xcol;
4337         int     exact;
4338 
4339         xcol.red = (short) (red * 65535.0);
4340         xcol.green = (short) (green * 65535.0);
4341         xcol.blue = (short) (blue * 65535.0);
4342 
4343         noFaultXAllocColor(Tk_Display(togl->TkWin), Tk_Colormap(togl->TkWin),
4344                 Tk_Visual(togl->TkWin)->map_entries, &xcol, &exact);
4345         /* for color index mode photos */
4346         togl->RedMap[xcol.pixel] = (float) xcol.red / 65535.0;
4347         togl->GreenMap[xcol.pixel] = (float) xcol.green / 65535.0;
4348         togl->BlueMap[xcol.pixel] = (float) xcol.blue / 65535.0;
4349 
4350         return xcol.pixel;
4351     }
4352 
4353 #elif defined(TOGL_WGL)
4354     return Win32AllocColor(togl, red, green, blue);
4355 
4356 #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
4357     /* still need to implement this on Mac... */
4358     return 0;
4359 
4360 #endif
4361 }
4362 
4363 
4364 
4365 void
Togl_FreeColor(const Togl * togl,unsigned long pixel)4366 Togl_FreeColor(const Togl *togl, unsigned long pixel)
4367 {
4368     if (togl->RgbaFlag) {
4369         (void) fprintf(stderr, "Error: Togl_FreeColor illegal in RGBA mode.\n");
4370         return;
4371     }
4372     /* TODO: maybe not... */
4373     if (togl->PrivateCmapFlag) {
4374         (void) fprintf(stderr,
4375                 "Error: Togl_FreeColor illegal with private colormap\n");
4376         return;
4377     }
4378 #if defined(TOGL_X11)
4379     (void) XFreeColors(Tk_Display(togl->TkWin), Tk_Colormap(togl->TkWin),
4380             &pixel, 1, 0);
4381 #elif defined(TOGL_WGL)
4382     Win32FreeColor(togl, pixel);
4383 #endif
4384 }
4385 
4386 
4387 
4388 void
Togl_SetColor(const Togl * togl,unsigned long index,float red,float green,float blue)4389 Togl_SetColor(const Togl *togl,
4390         unsigned long index, float red, float green, float blue)
4391 {
4392 
4393     if (togl->RgbaFlag) {
4394         (void) fprintf(stderr, "Error: Togl_SetColor illegal in RGBA mode.\n");
4395         return;
4396     }
4397     if (!togl->PrivateCmapFlag) {
4398         (void) fprintf(stderr,
4399                 "Error: Togl_SetColor requires a private colormap\n");
4400         return;
4401     }
4402 #if defined(TOGL_X11)
4403     {
4404         XColor  xcol;
4405 
4406         xcol.pixel = index;
4407         xcol.red = (short) (red * 65535.0);
4408         xcol.green = (short) (green * 65535.0);
4409         xcol.blue = (short) (blue * 65535.0);
4410         xcol.flags = DoRed | DoGreen | DoBlue;
4411 
4412         (void) XStoreColor(Tk_Display(togl->TkWin), Tk_Colormap(togl->TkWin),
4413                 &xcol);
4414 
4415         /* for color index mode photos */
4416         togl->RedMap[xcol.pixel] = (float) xcol.red / 65535.0;
4417         togl->GreenMap[xcol.pixel] = (float) xcol.green / 65535.0;
4418         togl->BlueMap[xcol.pixel] = (float) xcol.blue / 65535.0;
4419     }
4420 #elif defined(TOGL_WGL)
4421     Win32SetColor(togl, index, red, green, blue);
4422 #endif
4423 }
4424 
4425 
4426 #if TOGL_USE_FONTS == 1
4427 #  include "toglFont.c"
4428 #else
4429 
4430 Tcl_Obj *
Togl_LoadBitmapFont(const Togl * togl,const char * fontname)4431 Togl_LoadBitmapFont(const Togl *togl, const char *fontname)
4432 {
4433     return NULL;
4434 }
4435 
4436 int
Togl_UnloadBitmapFont(const Togl * togl,Tcl_Obj * bitmapfont)4437 Togl_UnloadBitmapFont(const Togl *togl, Tcl_Obj *bitmapfont)
4438 {
4439     return TCL_OK;
4440 }
4441 
4442 int
Togl_WriteObj(const Togl * togl,const Tcl_Obj * toglfont,Tcl_Obj * obj)4443 Togl_WriteObj(const Togl *togl, const Tcl_Obj *toglfont, Tcl_Obj *obj)
4444 {
4445     return -1;
4446 }
4447 
4448 int
Togl_WriteChars(const Togl * togl,const Tcl_Obj * toglfont,const char * str,int len)4449 Togl_WriteChars(const Togl *togl, const Tcl_Obj *toglfont, const char *str,
4450         int len)
4451 {
4452     return -1;
4453 }
4454 #endif /* TOGL_USE_FONTS */
4455 
4456 
4457 
4458 /*
4459  * Overlay functions
4460  */
4461 
4462 
4463 void
Togl_UseLayer(Togl * togl,int layer)4464 Togl_UseLayer(Togl *togl, int layer)
4465 {
4466     if (layer == TOGL_NORMAL) {
4467 #if defined(TOGL_WGL)
4468         int     res = wglMakeCurrent(togl->tglGLHdc, togl->Ctx);
4469 
4470         if (!res) {
4471             ErrorExit(TEXT("wglMakeCurrent"));
4472         }
4473 #elif defined(TOGL_X11)
4474         (void) glXMakeCurrent(Tk_Display(togl->TkWin),
4475                 Tk_WindowId(togl->TkWin), togl->Ctx);
4476 #elif defined(TOGL_AGL)
4477         (void) aglSetCurrentContext(togl->Ctx);
4478 #elif defined(TOGL_NSOPENGL)
4479         [togl->Ctx makeCurrentContext];
4480 #endif
4481     } else if (layer == TOGL_OVERLAY && togl->OverlayWindow) {
4482 #if defined(TOGL_WGL)
4483         int     res = wglMakeCurrent(togl->tglGLHdc, togl->tglGLOverlayHglrc);
4484 
4485         if (!res) {
4486             ErrorExit(TEXT("wglMakeCurrent overlay"));
4487         }
4488 #elif defined(TOGL_X11)
4489         (void) glXMakeCurrent(Tk_Display(togl->TkWin),
4490                 togl->OverlayWindow, togl->OverlayCtx);
4491 #elif defined(TOGL_AGL) || defined(TOGL_NSOPENGL)
4492 #endif
4493     } else {
4494         /* error */
4495     }
4496 }
4497 
4498 
4499 void
Togl_ShowOverlay(Togl * togl)4500 Togl_ShowOverlay(Togl *togl)
4501 {
4502 #if defined(TOGL_X11)           /* not yet implemented on Windows */
4503     if (togl->OverlayWindow) {
4504         (void) XMapWindow(Tk_Display(togl->TkWin), togl->OverlayWindow);
4505         (void) XInstallColormap(Tk_Display(togl->TkWin), togl->OverlayCmap);
4506         togl->OverlayIsMapped = True;
4507     }
4508 #endif
4509 }
4510 
4511 
4512 void
Togl_HideOverlay(Togl * togl)4513 Togl_HideOverlay(Togl *togl)
4514 {
4515     if (togl->OverlayWindow && togl->OverlayIsMapped) {
4516         (void) XUnmapWindow(Tk_Display(togl->TkWin), togl->OverlayWindow);
4517         togl->OverlayIsMapped = False;
4518     }
4519 }
4520 
4521 
4522 void
Togl_PostOverlayRedisplay(Togl * togl)4523 Togl_PostOverlayRedisplay(Togl *togl)
4524 {
4525     if (!togl->OverlayUpdatePending
4526             && togl->OverlayWindow && togl->OverlayDisplayProc) {
4527         Tk_DoWhenIdle(Togl_RenderOverlay, (ClientData) togl);
4528         togl->OverlayUpdatePending = True;
4529     }
4530 }
4531 
4532 
4533 int
Togl_ExistsOverlay(const Togl * togl)4534 Togl_ExistsOverlay(const Togl *togl)
4535 {
4536     return togl->OverlayFlag;
4537 }
4538 
4539 
4540 int
Togl_GetOverlayTransparentValue(const Togl * togl)4541 Togl_GetOverlayTransparentValue(const Togl *togl)
4542 {
4543     return togl->OverlayTransparentPixel;
4544 }
4545 
4546 
4547 int
Togl_IsMappedOverlay(const Togl * togl)4548 Togl_IsMappedOverlay(const Togl *togl)
4549 {
4550     return togl->OverlayFlag && togl->OverlayIsMapped;
4551 }
4552 
4553 
4554 unsigned long
Togl_AllocColorOverlay(const Togl * togl,float red,float green,float blue)4555 Togl_AllocColorOverlay(const Togl *togl, float red, float green, float blue)
4556 {
4557 #if defined(TOGL_X11)           /* not yet implemented on Windows */
4558     if (togl->OverlayFlag && togl->OverlayCmap) {
4559         XColor  xcol;
4560 
4561         xcol.red = (short) (red * 65535.0);
4562         xcol.green = (short) (green * 65535.0);
4563         xcol.blue = (short) (blue * 65535.0);
4564         if (!XAllocColor(Tk_Display(togl->TkWin), togl->OverlayCmap, &xcol))
4565             return (unsigned long) -1;
4566         return xcol.pixel;
4567     }
4568 #endif /* TOGL_X11 */
4569     return (unsigned long) -1;
4570 }
4571 
4572 
4573 void
Togl_FreeColorOverlay(const Togl * togl,unsigned long pixel)4574 Togl_FreeColorOverlay(const Togl *togl, unsigned long pixel)
4575 {
4576 #if defined(TOGL_X11)           /* not yet implemented on Windows */
4577     if (togl->OverlayFlag && togl->OverlayCmap) {
4578         (void) XFreeColors(Tk_Display(togl->TkWin), togl->OverlayCmap, &pixel,
4579                 1, 0);
4580     }
4581 #endif /* TOGL_X11 */
4582 }
4583 
4584 
4585 /*
4586  * User client data
4587  */
4588 
4589 ClientData
Togl_GetClientData(const Togl * togl)4590 Togl_GetClientData(const Togl *togl)
4591 {
4592     return togl->Client_Data;
4593 }
4594 
4595 
4596 void
Togl_SetClientData(Togl * togl,ClientData clientData)4597 Togl_SetClientData(Togl *togl, ClientData clientData)
4598 {
4599     togl->Client_Data = clientData;
4600 }
4601 
4602 int
Togl_CopyContext(const Togl * from,const Togl * to,unsigned mask)4603 Togl_CopyContext(const Togl *from, const Togl *to, unsigned mask)
4604 {
4605 #ifdef TOGL_X11
4606     int     error_code;
4607     int     same = (glXGetCurrentContext() == to->Ctx);
4608 
4609     if (same)
4610         (void) glXMakeCurrent(to->display, None, NULL);
4611     togl_SetupXErrorHandler();
4612     glXCopyContext(from->display, from->Ctx, to->Ctx, mask);
4613     if (error_code = togl_CheckForXError(from)) {
4614         char    buf[256];
4615 
4616         XGetErrorText(from->display, error_code, buf, sizeof buf);
4617         Tcl_AppendResult(from->Interp, "unable to copy context: ", buf, NULL);
4618         return TCL_ERROR;
4619     }
4620 #elif defined(TOGL_WGL)
4621     int     same = (wglGetCurrentContext() == to->Ctx);
4622 
4623     if (same)
4624         (void) wglMakeCurrent(to->tglGLHdc, NULL);
4625     if (!wglCopyContext(from->Ctx, to->Ctx, mask)) {
4626         char buf[256];
4627 
4628         snprintf(buf, sizeof buf, "unable to copy context: %d", GetLastError());
4629         Tcl_AppendElement(from->Interp, buf);
4630         return TCL_ERROR;
4631     }
4632 #elif defined(TOGL_AGL)
4633     int     same = (aglGetCurrentContext() == to->Ctx);
4634 
4635     if (same)
4636         (void) aglSetCurrentContext(NULL);
4637     if (!aglCopyContext(from->Ctx, to->Ctx, mask)) {
4638         Tcl_AppendResult(from->Interp, "unable to copy context: ",
4639                 aglErrorString(aglGetError()), NULL);
4640         return TCL_ERROR;
4641     }
4642 #elif defined(TOGL_NSOPENGL)
4643     int     same = (from->Ctx == to->Ctx);
4644 
4645     if (same) {
4646       [NSOpenGLContext clearCurrentContext];
4647     }
4648     [to->Ctx copyAttributesFromContext:from->Ctx withMask:mask];
4649 #endif
4650     if (same)
4651         Togl_MakeCurrent(to);
4652     return TCL_OK;
4653 }
4654 
4655 
4656 #ifdef MESA_COLOR_HACK
4657 /*
4658  * Let's know how many free colors do we have
4659  */
4660 #  define RLEVELS     5
4661 #  define GLEVELS     9
4662 #  define BLEVELS     5
4663 
4664 /* to free dithered_rgb_colormap pixels allocated by Mesa */
4665 static unsigned long *ToglMesaUsedPixelCells = NULL;
4666 static int ToglMesaUsedFreeCells = 0;
4667 
4668 static int
get_free_color_cells(Display * display,int screen,Colormap colormap)4669 get_free_color_cells(Display *display, int screen, Colormap colormap)
4670 {
4671     if (!ToglMesaUsedPixelCells) {
4672         XColor  xcol;
4673         int     i;
4674         int     colorsfailed, ncolors = XDisplayCells(display, screen);
4675 
4676         long    r, g, b;
4677 
4678         ToglMesaUsedPixelCells =
4679                 (unsigned long *) ckalloc(ncolors * sizeof (unsigned long));
4680 
4681         /* Allocate X colors and initialize color_table[], red_table[], etc */
4682         /* de Mesa 2.1: xmesa1.c setup_dithered_(...) */
4683         i = colorsfailed = 0;
4684         for (r = 0; r < RLEVELS; r++)
4685             for (g = 0; g < GLEVELS; g++)
4686                 for (b = 0; b < BLEVELS; b++) {
4687                     int     exact;
4688 
4689                     xcol.red = (r * 65535) / (RLEVELS - 1);
4690                     xcol.green = (g * 65535) / (GLEVELS - 1);
4691                     xcol.blue = (b * 65535) / (BLEVELS - 1);
4692                     noFaultXAllocColor(display, colormap, ncolors,
4693                             &xcol, &exact);
4694                     ToglMesaUsedPixelCells[i++] = xcol.pixel;
4695                     if (!exact) {
4696                         colorsfailed++;
4697                     }
4698                 }
4699         ToglMesaUsedFreeCells = i;
4700 
4701         XFreeColors(display, colormap, ToglMesaUsedPixelCells,
4702                 ToglMesaUsedFreeCells, 0x00000000);
4703     }
4704     return ToglMesaUsedFreeCells;
4705 }
4706 
4707 
4708 static void
free_default_color_cells(Display * display,Colormap colormap)4709 free_default_color_cells(Display *display, Colormap colormap)
4710 {
4711     if (ToglMesaUsedPixelCells) {
4712         XFreeColors(display, colormap, ToglMesaUsedPixelCells,
4713                 ToglMesaUsedFreeCells, 0x00000000);
4714         ckfree((char *) ToglMesaUsedPixelCells);
4715         ToglMesaUsedPixelCells = NULL;
4716         ToglMesaUsedFreeCells = 0;
4717     }
4718 }
4719 #endif
4720 
4721 /*
4722  * Original stereo code contributed by Ben Evans (Ben.Evans@anusf.anu.edu.au)
4723  * and was based on SGI's /usr/share/src/OpenGL/teach/stereo/glwstereo.c,
4724  * which is identical to the 1997/12/1 glwstereo.c code in the CrystalEyes
4725  * Software Development Kit.
4726  */
4727 
4728 int
Togl_NumEyes(const Togl * togl)4729 Togl_NumEyes(const Togl *togl)
4730 {
4731     if (togl->Stereo > TOGL_STEREO_ONE_EYE_MAX)
4732         return 2;
4733     return 1;
4734 }
4735 
4736 /* call instead of glDrawBuffer */
4737 void
Togl_DrawBuffer(Togl * togl,GLenum mode)4738 Togl_DrawBuffer(Togl *togl, GLenum mode)
4739 {
4740     if (togl->Stereo <= TOGL_STEREO_ONE_EYE_MAX) {
4741         /* Only drawing a single eye */
4742         if (togl->currentStereoBuffer != STEREO_BUFFER_NONE) {
4743             Togl_SetViewPort(togl);
4744             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
4745             togl->currentStereoBuffer = STEREO_BUFFER_NONE;
4746         }
4747         switch (mode) {
4748           case GL_FRONT:
4749           case GL_BACK:
4750           case GL_FRONT_AND_BACK:
4751               break;
4752           case GL_LEFT:
4753           case GL_FRONT_LEFT:
4754           case GL_RIGHT:
4755           case GL_FRONT_RIGHT:
4756               mode = GL_FRONT;
4757               break;
4758           case GL_BACK_LEFT:
4759           case GL_BACK_RIGHT:
4760               mode = GL_BACK;
4761               break;
4762           default:
4763               break;
4764         }
4765         glDrawBuffer(mode);
4766         return;
4767     }
4768     /* called once for each eye */
4769     switch (mode) {
4770       case GL_FRONT:
4771       case GL_BACK:
4772       case GL_FRONT_AND_BACK:
4773           /*
4774            ** Simultaneous drawing to both left and right buffers isn't
4775            ** really possible if we don't have a stereo capable visual.
4776            ** For now just fall through and use the left buffer.
4777            */
4778       case GL_LEFT:
4779       case GL_FRONT_LEFT:
4780       case GL_BACK_LEFT:
4781           togl->currentStereoBuffer = STEREO_BUFFER_LEFT;
4782           break;
4783       case GL_RIGHT:
4784       case GL_FRONT_RIGHT:
4785       case GL_BACK_RIGHT:
4786           togl->currentStereoBuffer = STEREO_BUFFER_RIGHT;
4787           break;
4788       default:
4789           break;
4790     }
4791     if (togl->Stereo != TOGL_STEREO_NATIVE) {
4792         switch (mode) {
4793           default:
4794               mode = GL_FRONT;
4795               break;
4796           case GL_BACK:
4797           case GL_BACK_LEFT:
4798           case GL_BACK_RIGHT:
4799               mode = GL_BACK;
4800               break;
4801         }
4802     }
4803     int w = togl->Width*togl->PixelScale, h = togl->Height*togl->PixelScale;
4804     switch (togl->Stereo) {
4805       default:
4806           break;
4807 #ifdef __sgi
4808       case TOGL_STEREO_SGIOLDSTYLE:
4809           glXWaitGL();          /* sync with GL command stream before calling X
4810                                  */
4811           XSGISetStereoBuffer(togl->display, Tk_WindowId(togl->TkWin),
4812                   togl->currentStereoBuffer);
4813           glXWaitX();           /* sync with X command stream before calling GL
4814                                  */
4815           break;
4816 #endif
4817       case TOGL_STEREO_ANAGLYPH:
4818           if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT)
4819               glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
4820           else
4821               glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
4822           glViewport(0, 0, w, h);
4823           break;
4824       case TOGL_STEREO_CROSS_EYE:
4825           if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT)
4826               glViewport(w / 2 + 1, 0, w / 2, h);
4827           else
4828               glViewport(0, 0, w / 2, h);
4829           break;
4830       case TOGL_STEREO_WALL_EYE:
4831           if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT)
4832               glViewport(0, 0, w / 2, h);
4833           else
4834               glViewport(w / 2 + 1, 0, w / 2, h);
4835           break;
4836       case TOGL_STEREO_DTI:
4837           if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT)
4838               glViewport(0, 0, w / 2, h);
4839           else
4840               glViewport(w / 2 + 1, 0, w / 2, h);
4841           break;
4842       case TOGL_STEREO_ROW_INTERLEAVED:
4843           glViewport(0, 0, w, h);
4844           break;
4845     }
4846     glDrawBuffer(mode);
4847 }
4848 
4849 /* call instead of glClear */
4850 void
Togl_Clear(const Togl * togl,GLbitfield mask)4851 Togl_Clear(const Togl *togl, GLbitfield mask)
4852 {
4853     GLint   stencil_write_mask = 0;
4854     GLint   stencil_clear_value = 0;
4855 
4856     switch (togl->Stereo) {
4857       default:
4858           break;
4859       case TOGL_STEREO_CROSS_EYE:
4860       case TOGL_STEREO_WALL_EYE:
4861       case TOGL_STEREO_DTI:
4862           if (togl->currentStereoBuffer != STEREO_BUFFER_LEFT) {
4863               /* Since glViewport does not affect what is cleared (unlike
4864                * glScissor), only clear in left eye */
4865               return;
4866           }
4867           break;
4868       case TOGL_STEREO_ROW_INTERLEAVED:
4869           if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) {
4870               if ((mask & GL_STENCIL_BUFFER_BIT) == 0) {
4871                   mask |= GL_STENCIL_BUFFER_BIT;
4872                   glStencilMask(~0u);
4873                   glClearStencil(0);
4874               } else {
4875                   glGetIntegerv(GL_STENCIL_WRITEMASK, &stencil_write_mask);
4876                   glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencil_clear_value);
4877                   glStencilMask(stencil_write_mask | togl->riStencilBit);
4878                   glClearStencil(stencil_clear_value & ~togl->riStencilBit);
4879               }
4880           } else {
4881               mask &= ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
4882           }
4883           break;
4884     }
4885 #if 0
4886     /* only needed if we wish to support multi-eye clears */
4887     if (togl->Stereo > TOGL_STEREO_ONE_EYE_MAX) {
4888         GLenum  drawBuffer = togl->currentDrawBuffer;
4889 
4890         switch (drawBuffer) {
4891           case GL_FRONT:
4892               Togl_DrawBuffer(togl, GL_FRONT_RIGHT);
4893               glClear(mask);
4894               Togl_DrawBuffer(togl, drawBuffer);
4895               break;
4896           case GL_BACK:
4897               Togl_DrawBuffer(togl, GL_BACK_RIGHT);
4898               glClear(mask);
4899               Togl_DrawBuffer(togl, drawBuffer);
4900               break;
4901           case GL_FRONT_AND_BACK:
4902               Togl_DrawBuffer(togl, GL_RIGHT);
4903               glClear(mask);
4904               Togl_DrawBuffer(togl, drawBuffer);
4905               break;
4906           case GL_LEFT:
4907           case GL_FRONT_LEFT:
4908           case GL_BACK_LEFT:
4909           case GL_RIGHT:
4910           case GL_FRONT_RIGHT:
4911           case GL_BACK_RIGHT:
4912           default:
4913               break;
4914         }
4915     }
4916 #endif
4917     if (mask != 0)
4918         glClear(mask);
4919     if (togl->Stereo == TOGL_STEREO_ROW_INTERLEAVED) {
4920         int     x, y;
4921 
4922         if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT) {
4923             int     i;
4924 
4925             /* initialize stencil buffer mask */
4926             glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT
4927                     | GL_LINE_BIT | GL_VIEWPORT_BIT);
4928             // 2d projection
4929             Togl_SetViewPort(togl);
4930             glMatrixMode(GL_PROJECTION);
4931             glPushMatrix();
4932             glLoadIdentity();
4933             glOrtho(0, togl->Width, 0, togl->Height, -1, 1);
4934             glMatrixMode(GL_MODELVIEW);
4935             glPushMatrix();
4936             glLoadIdentity();
4937             glTranslatef(0.375f, 0.375f, 0);
4938             glDisable(GL_ALPHA_TEST);
4939             glDisable(GL_COLOR_LOGIC_OP);
4940             glDisable(GL_DEPTH_TEST);
4941             glDisable(GL_DITHER);
4942             glDisable(GL_INDEX_LOGIC_OP);
4943             glDisable(GL_LIGHTING);
4944             glDisable(GL_LINE_SMOOTH);
4945             glDisable(GL_MULTISAMPLE);
4946             glLineWidth(1);
4947             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4948             glStencilFunc(GL_ALWAYS, togl->riStencilBit, togl->riStencilBit);
4949             glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
4950             glBegin(GL_LINES);
4951             for (i = 0; i < togl->Height; i += 2) {
4952                 glVertex2i(0, i);
4953                 glVertex2i(togl->Width, i);
4954             }
4955             glEnd();
4956             glMatrixMode(GL_PROJECTION);
4957             glPopMatrix();
4958             glMatrixMode(GL_MODELVIEW);
4959             glPopMatrix();
4960             glPopAttrib();
4961 
4962             if (stencil_write_mask) {
4963                 glStencilMask(stencil_write_mask & ~togl->riStencilBit);
4964             } else {
4965                 glStencilMask(~togl->riStencilBit);
4966             }
4967 
4968             Tk_GetRootCoords(togl->TkWin, &x, &y);
4969             if ((y + togl->Height) % 2) {
4970                 glStencilFunc(GL_NOTEQUAL, togl->riStencilBit,
4971                         togl->riStencilBit);
4972             } else {
4973                 glStencilFunc(GL_EQUAL, togl->riStencilBit, togl->riStencilBit);
4974             }
4975         } else {
4976             Tk_GetRootCoords(togl->TkWin, &x, &y);
4977             if ((y + togl->Height) % 2) {
4978                 glStencilFunc(GL_EQUAL, togl->riStencilBit, togl->riStencilBit);
4979             } else {
4980                 glStencilFunc(GL_NOTEQUAL, togl->riStencilBit,
4981                         togl->riStencilBit);
4982             }
4983         }
4984     }
4985 }
4986 
4987 /*
4988  * Togl_Frustum and Togl_Ortho:
4989  *
4990  *     eyeOffset is the distance from the center line
4991  *     and is negative for the left eye and positive for right eye.
4992  *     eyeDist and eyeOffset need to be in the same units as your model space.
4993  *     In physical space, eyeDist might be 30 inches from the screen
4994  *     and eyeDist would be +/- 1.25 inch (for a total interocular distance
4995  *     of 2.5 inches).
4996  */
4997 
4998 void
Togl_Frustum(const Togl * togl,GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble zNear,GLdouble zFar)4999 Togl_Frustum(const Togl *togl, GLdouble left, GLdouble right,
5000         GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
5001 {
5002     GLdouble eyeOffset = 0, eyeShift = 0;
5003 
5004     if (togl->Stereo == TOGL_STEREO_LEFT_EYE
5005             || togl->currentStereoBuffer == STEREO_BUFFER_LEFT)
5006         eyeOffset = -togl->EyeSeparation / 2;   /* for left eye */
5007     else if (togl->Stereo == TOGL_STEREO_RIGHT_EYE
5008             || togl->currentStereoBuffer == STEREO_BUFFER_RIGHT)
5009         eyeOffset = togl->EyeSeparation / 2;    /* for right eye */
5010     eyeShift = (togl->Convergence - zNear) * (eyeOffset / togl->Convergence);
5011 
5012     /* compenstate for altered viewports */
5013     switch (togl->Stereo) {
5014       default:
5015           break;
5016       case TOGL_STEREO_SGIOLDSTYLE:
5017       case TOGL_STEREO_DTI:
5018           /* squished image is expanded, nothing needed */
5019           break;
5020       case TOGL_STEREO_CROSS_EYE:
5021       case TOGL_STEREO_WALL_EYE:{
5022           GLdouble delta = (top - bottom) / 2;
5023 
5024           top += delta;
5025           bottom -= delta;
5026           break;
5027       }
5028     }
5029 
5030     glFrustum(left + eyeShift, right + eyeShift, bottom, top, zNear, zFar);
5031     glTranslated(-eyeShift, 0, 0);
5032 }
5033 
5034 void
Togl_Ortho(const Togl * togl,GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble zNear,GLdouble zFar)5035 Togl_Ortho(const Togl *togl, GLdouble left, GLdouble right,
5036         GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
5037 {
5038     /* TODO: debug this */
5039     GLdouble eyeOffset = 0, eyeShift = 0;
5040 
5041     if (togl->currentStereoBuffer == STEREO_BUFFER_LEFT)
5042         eyeOffset = -togl->EyeSeparation / 2;   /* for left eye */
5043     else if (togl->currentStereoBuffer == STEREO_BUFFER_RIGHT)
5044         eyeOffset = togl->EyeSeparation / 2;    /* for right eye */
5045     eyeShift = (togl->Convergence - zNear) * (eyeOffset / togl->Convergence);
5046 
5047     /* compenstate for altered viewports */
5048     switch (togl->Stereo) {
5049       default:
5050           break;
5051       case TOGL_STEREO_SGIOLDSTYLE:
5052       case TOGL_STEREO_DTI:
5053           /* squished image is expanded, nothing needed */
5054           break;
5055       case TOGL_STEREO_CROSS_EYE:
5056       case TOGL_STEREO_WALL_EYE:{
5057           GLdouble delta = (top - bottom) / 2;
5058 
5059           top += delta;
5060           bottom -= delta;
5061           break;
5062       }
5063     }
5064 
5065     glOrtho(left + eyeShift, right + eyeShift, bottom, top, zNear, zFar);
5066     glTranslated(-eyeShift, 0, 0);
5067 }
5068 
5069 int
Togl_GetToglFromObj(Tcl_Interp * interp,Tcl_Obj * obj,Togl ** toglPtr)5070 Togl_GetToglFromObj(Tcl_Interp *interp, Tcl_Obj *obj, Togl **toglPtr)
5071 {
5072     Tcl_Command toglCmd;
5073     Tcl_CmdInfo info;
5074 
5075     toglCmd = Tcl_GetCommandFromObj(interp, obj);
5076     if (Tcl_GetCommandInfoFromToken(toglCmd, &info) == 0
5077             || info.objProc != Togl_ObjWidget) {
5078         Tcl_AppendResult(interp, "expected togl command argument", NULL);
5079         return TCL_ERROR;
5080     }
5081     *toglPtr = (Togl *) info.objClientData;
5082     return TCL_OK;
5083 }
5084 
5085 int
Togl_GetToglFromName(Tcl_Interp * interp,const char * cmdName,Togl ** toglPtr)5086 Togl_GetToglFromName(Tcl_Interp *interp, const char *cmdName, Togl **toglPtr)
5087 {
5088     Tcl_CmdInfo info;
5089 
5090     if (Tcl_GetCommandInfo(interp, cmdName, &info) == 0
5091             || info.objProc != Togl_ObjWidget) {
5092         Tcl_AppendResult(interp, "expected togl command argument", NULL);
5093         return TCL_ERROR;
5094     }
5095     *toglPtr = (Togl *) info.objClientData;
5096     return TCL_OK;
5097 }
5098 
5099 static int ObjectIsEmpty(Tcl_Obj *objPtr);
5100 
5101 /*
5102  *----------------------------------------------------------------------
5103  *
5104  * GetStereo -
5105  *
5106  *      Converts an internal int into a a Tcl string obj.
5107  *
5108  * Results:
5109  *      Tcl_Obj containing the string representation of the stereo value.
5110  *
5111  * Side effects:
5112  *      Creates a new Tcl_Obj.
5113  *
5114  *----------------------------------------------------------------------
5115  */
5116 
5117 static Tcl_Obj *
GetStereo(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)5118 GetStereo(ClientData clientData, Tk_Window tkwin, char *recordPtr,
5119         int internalOffset)
5120     /* recordPtr is a pointer to widget record. */
5121     /* internalOffset is the offset within *recordPtr containing the stereo
5122      * value. */
5123 {
5124     int     stereo = *(int *) (recordPtr + internalOffset);
5125     const char *name = "unknown";
5126 
5127     switch (stereo) {
5128       case TOGL_STEREO_NONE:
5129           name = "";
5130           break;
5131       case TOGL_STEREO_LEFT_EYE:
5132           name = "left eye";
5133           break;
5134       case TOGL_STEREO_RIGHT_EYE:
5135           name = "right eye";
5136           break;
5137       case TOGL_STEREO_NATIVE:
5138           name = "native";
5139           break;
5140       case TOGL_STEREO_SGIOLDSTYLE:
5141           name = "sgioldstyle";
5142           break;
5143       case TOGL_STEREO_ANAGLYPH:
5144           name = "anaglyph";
5145           break;
5146       case TOGL_STEREO_CROSS_EYE:
5147           name = "cross-eye";
5148           break;
5149       case TOGL_STEREO_WALL_EYE:
5150           name = "wall-eye";
5151           break;
5152       case TOGL_STEREO_DTI:
5153           name = "dti";
5154           break;
5155       case TOGL_STEREO_ROW_INTERLEAVED:
5156           name = "row interleaved";
5157           break;
5158     }
5159     return Tcl_NewStringObj(name, -1);
5160 }
5161 
5162 /*
5163  *----------------------------------------------------------------------
5164  *
5165  * SetStereo --
5166  *
5167  *      Converts a Tcl_Obj representing a widgets stereo into an
5168  *      integer value.
5169  *
5170  * Results:
5171  *      Standard Tcl result.
5172  *
5173  * Side effects:
5174  *      May store the integer value into the internal representation
5175  *      pointer.  May change the pointer to the Tcl_Obj to NULL to indicate
5176  *      that the specified string was empty and that is acceptable.
5177  *
5178  *----------------------------------------------------------------------
5179  */
5180 
5181 static int
SetStereo(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * oldInternalPtr,int flags)5182 SetStereo(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin,
5183         Tcl_Obj **value, char *recordPtr, int internalOffset,
5184         char *oldInternalPtr, int flags)
5185     /* interp is the current interp; may be used for errors. */
5186     /* tkwin is the Window for which option is being set. */
5187     /* value is a pointer to the pointer to the value object. We use a pointer
5188      * to the pointer because we may need to return a value (NULL). */
5189     /* recordPtr is a pointer to storage for the widget record. */
5190     /* internalOffset is the offset within *recordPtr at which the internal
5191      * value is to be stored. */
5192     /* oldInternalPtr is a pointer to storage for the old value. */
5193     /* flags are the flags for the option, set Tk_SetOptions. */
5194 {
5195     int     stereo = 0;
5196     char   *string, *internalPtr;
5197 
5198     internalPtr = (internalOffset > 0) ? recordPtr + internalOffset : NULL;
5199 
5200     if ((flags & TK_OPTION_NULL_OK) && ObjectIsEmpty(*value)) {
5201         *value = NULL;
5202     } else {
5203         /*
5204          * Convert the stereo specifier into an integer value.
5205          */
5206 
5207         if (Tcl_GetBooleanFromObj(NULL, *value, &stereo) == TCL_OK) {
5208             stereo = stereo ? TOGL_STEREO_NATIVE : TOGL_STEREO_NONE;
5209         } else {
5210             string = Tcl_GetString(*value);
5211 
5212             if (strcmp(string, "") == 0 || strcasecmp(string, "none") == 0) {
5213                 stereo = TOGL_STEREO_NONE;
5214             } else if (strcasecmp(string, "native") == 0) {
5215                 stereo = TOGL_STEREO_NATIVE;
5216                 /* check if available when creating visual */
5217             } else if (strcasecmp(string, "left eye") == 0) {
5218                 stereo = TOGL_STEREO_LEFT_EYE;
5219             } else if (strcasecmp(string, "right eye") == 0) {
5220                 stereo = TOGL_STEREO_RIGHT_EYE;
5221             } else if (strcasecmp(string, "sgioldstyle") == 0) {
5222                 stereo = TOGL_STEREO_SGIOLDSTYLE;
5223             } else if (strcasecmp(string, "anaglyph") == 0) {
5224                 stereo = TOGL_STEREO_ANAGLYPH;
5225             } else if (strcasecmp(string, "cross-eye") == 0) {
5226                 stereo = TOGL_STEREO_CROSS_EYE;
5227             } else if (strcasecmp(string, "wall-eye") == 0) {
5228                 stereo = TOGL_STEREO_WALL_EYE;
5229             } else if (strcasecmp(string, "dti") == 0) {
5230                 stereo = TOGL_STEREO_DTI;
5231             } else if (strcasecmp(string, "row interleaved") == 0) {
5232                 stereo = TOGL_STEREO_ROW_INTERLEAVED;
5233             } else {
5234                 Tcl_ResetResult(interp);
5235                 Tcl_AppendResult(interp, "bad stereo value \"",
5236                         Tcl_GetString(*value), "\"", NULL);
5237                 return TCL_ERROR;
5238             }
5239         }
5240     }
5241 
5242     if (internalPtr != NULL) {
5243         *((int *) oldInternalPtr) = *((int *) internalPtr);
5244         *((int *) internalPtr) = stereo;
5245     }
5246     return TCL_OK;
5247 }
5248 
5249 /*
5250  *----------------------------------------------------------------------
5251  * RestoreStereo --
5252  *
5253  *      Restore a stereo option value from a saved value.
5254  *
5255  * Results:
5256  *      None.
5257  *
5258  * Side effects:
5259  *      Restores the old value.
5260  *
5261  *----------------------------------------------------------------------
5262  */
5263 
5264 static void
RestoreStereo(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * oldInternalPtr)5265 RestoreStereo(ClientData clientData, Tk_Window tkwin, char *internalPtr,
5266         char *oldInternalPtr)
5267 {
5268     *(int *) internalPtr = *(int *) oldInternalPtr;
5269 }
5270 
5271 /*
5272  *----------------------------------------------------------------------
5273  *
5274  * GetWideInt -
5275  *
5276  *      Converts an internal wide integer into a a Tcl WideInt obj.
5277  *
5278  * Results:
5279  *      Tcl_Obj containing the wide int value.
5280  *
5281  * Side effects:
5282  *      Creates a new Tcl_Obj.
5283  *
5284  *----------------------------------------------------------------------
5285  */
5286 
5287 static Tcl_Obj *
GetWideInt(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)5288 GetWideInt(ClientData clientData, Tk_Window tkwin, char *recordPtr,
5289         int internalOffset)
5290     /* recordPtr is a pointer to widget record. */
5291     /* internalOffset is the offset within *recordPtr containing the wide int
5292      * value. */
5293 {
5294     Tcl_WideInt wi = *(Tcl_WideInt *) (recordPtr + internalOffset);
5295 
5296     return Tcl_NewWideIntObj(wi);
5297 }
5298 
5299 /*
5300  *----------------------------------------------------------------------
5301  *
5302  * SetWideInt --
5303  *
5304  *      Converts a Tcl_Obj representing a Tcl_WideInt.
5305  *
5306  * Results:
5307  *      Standard Tcl result.
5308  *
5309  * Side effects:
5310  *      May store the wide int value into the internal representation
5311  *      pointer.  May change the pointer to the Tcl_Obj to NULL to indicate
5312  *      that the specified string was empty and that is acceptable.
5313  *
5314  *----------------------------------------------------------------------
5315  */
5316 
5317 static int
SetWideInt(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * oldInternalPtr,int flags)5318 SetWideInt(ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin,
5319         Tcl_Obj **value, char *recordPtr, int internalOffset,
5320         char *oldInternalPtr, int flags)
5321     /* interp is the current interp; may be used for errors. */
5322     /* tkwin is the Window for which option is being set. */
5323     /* value is a pointer to the pointer to the value object. We use a pointer
5324      * to the pointer because we may need to return a value (NULL). */
5325     /* recordPtr is a pointer to storage for the widget record. */
5326     /* internalOffset is the offset within *recordPtr at which the internal
5327      * value is to be stored. */
5328     /* oldInternalPtr is a pointer to storage for the old value. */
5329     /* flags are the flags for the option, set Tk_SetOptions. */
5330 {
5331     char   *internalPtr;
5332     Tcl_WideInt w;
5333 
5334     internalPtr = (internalOffset > 0) ? recordPtr + internalOffset : NULL;
5335 
5336     if ((flags & TK_OPTION_NULL_OK) && ObjectIsEmpty(*value)) {
5337         *value = NULL;
5338         w = 0;
5339     } else {
5340         if (Tcl_GetWideIntFromObj(interp, *value, &w) != TCL_OK) {
5341             return TCL_ERROR;
5342         }
5343     }
5344 
5345     if (internalPtr != NULL) {
5346         *((Tcl_WideInt *) oldInternalPtr) = *((Tcl_WideInt *) internalPtr);
5347         *((Tcl_WideInt *) internalPtr) = w;
5348     }
5349     return TCL_OK;
5350 }
5351 
5352 /*
5353  *----------------------------------------------------------------------
5354  * RestoreWideInt --
5355  *
5356  *      Restore a wide int option value from a saved value.
5357  *
5358  * Results:
5359  *      None.
5360  *
5361  * Side effects:
5362  *      Restores the old value.
5363  *
5364  *----------------------------------------------------------------------
5365  */
5366 
5367 static void
RestoreWideInt(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * oldInternalPtr)5368 RestoreWideInt(ClientData clientData, Tk_Window tkwin, char *internalPtr,
5369         char *oldInternalPtr)
5370 {
5371     *(Tcl_WideInt *) internalPtr = *(Tcl_WideInt *) oldInternalPtr;
5372 }
5373 
5374 /*
5375  *----------------------------------------------------------------------
5376  *
5377  * ObjectIsEmpty --
5378  *
5379  *      This procedure tests whether the string value of an object is
5380  *      empty.
5381  *
5382  * Results:
5383  *      The return value is 1 if the string value of objPtr has length
5384  *      zero, and 0 otherwise.
5385  *
5386  * Side effects:
5387  *      None.
5388  *
5389  *----------------------------------------------------------------------
5390  */
5391 
5392 static int
ObjectIsEmpty(Tcl_Obj * objPtr)5393 ObjectIsEmpty(Tcl_Obj *objPtr)
5394 /* objPtr = Object to test.  May be NULL. */
5395 {
5396     int     length;
5397 
5398     if (objPtr == NULL) {
5399         return 1;
5400     }
5401     if (objPtr->bytes != NULL) {
5402         return (objPtr->length == 0);
5403     }
5404     Tcl_GetStringFromObj(objPtr, &length);
5405     return (length == 0);
5406 }
5407