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