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