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