1// @configure_input@
2
3/**************************************************************************\
4 * Copyright (c) Kongsberg Oil & Gas Technologies AS
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33\**************************************************************************/
34
35/**************************************************************************\
36 *
37 *  A WORD OF ADVICE
38 *
39 *  It is fruitless to modify the contents of the So@Gui@RenderArea.cpp file
40 *  because it is autogenerated by configure from the SoGuiRenderArea.cpp.in
41 *  file which you will find in the src/Inventor/@Gui@/common/ directory.
42 *  Do your modifications to that file instead.
43 *
44\**************************************************************************/
45
46// *************************************************************************
47
48/*!
49  \class So@Gui@RenderArea Inventor/@Gui@/So@Gui@RenderArea.h
50  \brief The So@Gui@RenderArea class adds scenegraph handling and event management.
51
52  \ingroup components viewers
53
54  The So@Gui@RenderArea class is a component that adds scenegraph
55  management and input device event handling to the So@Gui@GLWidget
56  component.
57
58  The class has many convenient methods for controlling aspects of the
59  rendering, like for instance transparency, aliasing and for
60  scheduling of redraws.
61
62  Native toolkit events are caught by So@Gui@RenderArea components,
63  translated to Coin SoEvent instances and passed on to the
64  scenegraph, in case the user is doing interactive operations on for
65  instance Coin geometry draggers.
66
67
68  So@Gui@RenderArea is the first non-abstract component in it's
69  inheritance hierarchy that you can use directly from client
70  application code to set up a scenegraph viewer canvas.
71
72  For an So@Gui@RenderArea component to properly display your
73  scenegraph, it must contain an SoCamera-derived node and at least
74  one SoLight-derived lightsource node.
75
76  Here's a complete, stand-alone example on how to set up an
77  So@Gui@RenderArea with a scenegraph:
78
79  \code
80  #include <Inventor/@Gui@/So@Gui@.h>
81  #include <Inventor/@Gui@/So@Gui@RenderArea.h>
82
83  #include <Inventor/nodes/SoCube.h>
84  #include <Inventor/nodes/SoRotor.h>
85  #include <Inventor/nodes/SoArray.h>
86  #include <Inventor/nodes/SoDirectionalLight.h>
87  #include <Inventor/nodes/SoPerspectiveCamera.h>
88  #include <Inventor/nodes/SoSeparator.h>
89
90  // Set up a simple scenegraph, just for demonstration purposes.
91  static SoSeparator *
92  get_scene_graph(void)
93  {
94    SoSeparator * root = new SoSeparator;
95
96    SoGroup * group = new SoGroup;
97
98    SoRotor * rotor = new SoRotor;
99    rotor->rotation = SbRotation(SbVec3f(0.2, 0.5, 0.9), M_PI/4.0);
100    group->addChild(rotor);
101
102    SoCube * cube = new SoCube;
103    group->addChild(cube);
104
105    SoArray * array = new SoArray;
106    array->origin = SoArray::CENTER;
107    array->addChild(group);
108    array->numElements1 = 2;
109    array->numElements2 = 2;
110    array->separation1 = SbVec3f(4, 0, 0);
111    array->separation2 = SbVec3f(0, 4, 0);
112
113    root->addChild(array);
114    return root;
115  }
116
117  int
118  main(int argc, char ** argv)
119  {
120    @WIDGET@ window = So@Gui@::init(argv[0]);
121
122    SoSeparator * root = new SoSeparator;
123    root->ref();
124
125    SoPerspectiveCamera * camera;
126    root->addChild(camera = new SoPerspectiveCamera);
127
128    root->addChild(new SoDirectionalLight);
129
130    SoSeparator * userroot = get_scene_graph();
131    root->addChild(userroot);
132
133    So@Gui@RenderArea * renderarea = new So@Gui@RenderArea(window);
134    camera->viewAll(userroot, renderarea->getViewportRegion());
135    renderarea->setSceneGraph(root);
136    renderarea->setBackgroundColor(SbColor(0.0f, 0.2f, 0.3f));
137    if (argc > 1) {
138      renderarea->setTitle(argv[1]);
139      renderarea->setIconTitle(argv[1]);
140    }
141    renderarea->show();
142
143    So@Gui@::show(window);
144    So@Gui@::mainLoop();
145
146    delete renderarea;
147    root->unref();
148
149    return 0;
150  }
151  \endcode
152*/
153
154// *************************************************************************
155
156#include <Inventor/@Gui@/So@Gui@RenderArea.h>
157
158#include <string.h> // strchr()
159
160#ifdef HAVE_CONFIG_H
161#include <config.h>
162#endif // HAVE_CONFIG_H
163
164#if SOQT_DEBUG // For the "soinfo" debugging backdoor.
165#include <qgl.h>
166#include <qapplication.h>
167#endif // SOQT_DEBUG
168
169#include <Inventor/@Gui@/common/gl.h> // glDrawBuffer()
170
171#include <Inventor/SoSceneManager.h>
172#include <Inventor/actions/SoSearchAction.h>
173#include <Inventor/actions/SoWriteAction.h>
174#include <Inventor/errors/SoDebugError.h>
175#include <Inventor/events/SoKeyboardEvent.h>
176#include <Inventor/misc/SoBasic.h>
177#include <Inventor/nodekits/SoBaseKit.h>
178#include <Inventor/nodes/SoCamera.h>
179#include <Inventor/nodes/SoSelection.h>
180#include <Inventor/SoOffscreenRenderer.h>
181#include <Inventor/SbColor4f.h>
182
183#include <so@gui@defs.h>
184#include <Inventor/@Gui@/So@Gui@Basic.h>
185#include <Inventor/@Gui@/So@Gui@.h>
186#include <Inventor/@Gui@/devices/So@Gui@Keyboard.h>
187#include <Inventor/@Gui@/devices/So@Gui@Mouse.h>
188#ifdef HAVE_JOYSTICK_LINUX
189#include <Inventor/@Gui@/devices/So@Gui@LinuxJoystick.h>
190#endif // HAVE_JOYSTICK_LINUX
191#ifdef HAVE_SPACENAV_SUPPORT
192#include <Inventor/@Gui@/devices/So@Gui@SpacenavDevice.h>
193#endif // HAVE_SPACENAV_SUPPORT
194
195#include <Inventor/@Gui@/So@Gui@GLWidgetP.h>
196#include <Inventor/@Gui@/SoAny.h>
197
198#define RENDERAREA_DEBUG_REDRAWS 0
199
200#define PRIVATE(obj) ((obj)->pimpl)
201#define PUBLIC(obj) ((obj)->pub)
202
203// *************************************************************************
204
205SO@GUI@_OBJECT_SOURCE(So@Gui@RenderArea);
206
207// *************************************************************************
208
209#ifndef DOXYGEN_SKIP_THIS
210
211class So@Gui@RenderAreaP {
212public:
213
214  So@Gui@RenderAreaP(class So@Gui@RenderArea * pub);
215  ~So@Gui@RenderAreaP(void);
216
217  SbBool clear;
218  SbBool clearZBuffer;
219  SbBool clearOverlay;
220
221  SoSceneManager * normalManager;
222  SoSceneManager * overlayManager;
223
224  SbColor * normalColormap;
225  int normalColormapSize;
226  int normalColormapStart;
227  SbColor * overlayColormap;
228  int overlayColormapSize;
229  int overlayColormapStart;
230
231  SbPList * devicelist;
232
233  struct {
234    So@Gui@Keyboard * keyboard;
235    So@Gui@Mouse * mouse;
236#ifdef HAVE_SPACENAV_SUPPORT
237    So@Gui@SpacenavDevice * spacenav;
238#endif // HAVE_SPACENAV_SUPPORT
239  } devices;
240
241  SbBool autoRedraw;
242
243  void replaceSoSelectionMonitor(SoSelection * newsel, SoSelection * oldsel) const;
244  SoSelection * normalselection;
245  SoSelection * overlayselection;
246
247  static const int GL_DEFAULT_MODE;
248
249  void constructor(SbBool mouseInput, SbBool keyboardInput, SbBool build);
250  static void renderCB(void * user, SoSceneManager * manager);
251  static void selection_redraw_cb(void * data, SoSelection * sel);
252  void setDevicesWindowSize(const SbVec2s size);
253
254  // OpenGL info-window hack.
255  enum { NONE, OPENGL, INVENTOR, TOOLKIT, DUMPSCENEGRAPH, DUMPCAMERAS, OFFSCREENGRAB };
256  int checkMagicSequences(const char c);
257  void showOpenGLDriverInformation(void);
258  void showInventorInformation(void);
259  void showToolkitInformation(void);
260  void dumpScenegraph(void);
261  void dumpCameras(void);
262  void offScreenGrab(void);
263
264  SbBool invokeAppCB(@EVENT@ event);
265  const SoEvent * getSoEvent(@EVENT@ event);
266
267  So@Gui@RenderAreaEventCB * appeventhandler;
268  void * appeventhandlerdata;
269
270private:
271  So@Gui@RenderArea * pub; // public interface class
272  SbString currentinput; // For the OpenGL info-window hack.
273};
274
275const int So@Gui@RenderAreaP::GL_DEFAULT_MODE = (SO_GL_RGB |
276                                                 SO_GL_ZBUFFER |
277                                                 SO_GL_DOUBLE );
278
279#if SO@GUI@_DEBUG && defined(__COIN__)
280// Disabled when compiling against SGI / TGS Inventor, as we're using
281// our Coin-specific extension SbString::sprintf() a lot.
282#define DEBUGGING_EGGS 1
283#endif // SO@GUI@_DEBUG && __COIN__
284
285// Note: assumes a valid current OpenGL context.
286void
287So@Gui@RenderAreaP::showOpenGLDriverInformation(void)
288{
289#if DEBUGGING_EGGS
290  const GLubyte * vendor = glGetString(GL_VENDOR);
291  const GLubyte * renderer = glGetString(GL_RENDERER);
292  const GLubyte * version = glGetString(GL_VERSION);
293  const GLubyte * extensions = glGetString(GL_EXTENSIONS);
294
295  SbString info = "GL_VENDOR: \""; info += (const char *)vendor; info += "\"\n";
296  info += "GL_RENDERER: \""; info += (const char *)renderer; info += "\"\n";
297  info += "GL_VERSION: \""; info += (const char *)version; info += "\"\n";
298  info += "GL_EXTENSIONS: \"\n   ";
299
300  SbString exts = (const char *)extensions;
301  const char * p;
302  int count = 0;
303  // (the extra parentheses in the while-expression kills a gcc warning)
304  while ((p = strchr(exts.getString(), ' '))) {
305    const char * start = exts.getString();
306    info += exts.getSubString(0, p - start);
307    exts.deleteSubString(0, p - start);
308    count++;
309    if (count == 4) { // number of extensions listed on each line
310      info += "\n   ";
311      count = 0;
312    }
313  }
314  if (exts.getLength() > 0) { info += "\n   "; info += exts; }
315  info += "\"\n";
316
317  // FIXME: should also show available GLX / WGL / AGL
318  // extensions. 20020802 mortene.
319
320  // Misc implementation info
321  {
322    SbVec2f range;
323    float granularity;
324    PUBLIC(this)->getPointSizeLimits(range, granularity);
325
326    SbString s;
327    s.sprintf("glPointSize(): range=[%f, %f], granularity=%f\n",
328              range[0], range[1], granularity);
329    info += s;
330
331
332    PUBLIC(this)->getLineWidthLimits(range, granularity);
333
334    s.sprintf("glLineWidth(): range=[%f, %f], granularity=%f\n",
335              range[0], range[1], granularity);
336    info += s;
337
338    GLint depthbits[1];
339    glGetIntegerv(GL_DEPTH_BITS, depthbits);
340    s.sprintf("GL_DEPTH_BITS==%d\n", depthbits[0]);
341    info += s;
342
343    GLint colbits[4];
344    glGetIntegerv(GL_RED_BITS, &colbits[0]);
345    glGetIntegerv(GL_GREEN_BITS, &colbits[1]);
346    glGetIntegerv(GL_BLUE_BITS, &colbits[2]);
347    glGetIntegerv(GL_ALPHA_BITS, &colbits[3]);
348    s.sprintf("GL_[RED|GREEN|BLUE|ALPHA]_BITS==[%d, %d, %d, %d]\n",
349              colbits[0], colbits[1], colbits[2], colbits[3]);
350    info += s;
351
352    GLint accumbits[4];
353    glGetIntegerv(GL_ACCUM_RED_BITS, &accumbits[0]);
354    glGetIntegerv(GL_ACCUM_GREEN_BITS, &accumbits[1]);
355    glGetIntegerv(GL_ACCUM_BLUE_BITS, &accumbits[2]);
356    glGetIntegerv(GL_ACCUM_ALPHA_BITS, &accumbits[3]);
357    s.sprintf("GL_ACCUM_[RED|GREEN|BLUE|ALPHA]_BITS==[%d, %d, %d, %d]\n",
358              accumbits[0], accumbits[1], accumbits[2], accumbits[3]);
359    info += s;
360
361    GLint stencilbits;
362    glGetIntegerv(GL_STENCIL_BITS, &stencilbits);
363    s.sprintf("GL_STENCIL_BITS==%d\n", stencilbits);
364    info += s;
365
366    GLint maxdims[2];
367    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxdims);
368    s.sprintf("GL_MAX_VIEWPORT_DIMS==<%d, %d>\n", maxdims[0], maxdims[1]);
369    info += s;
370
371    GLint texdim;
372    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texdim);
373    s.sprintf("GL_MAX_TEXTURE_SIZE==%d\n", texdim);
374    info += s;
375
376    GLint maxlights;
377    glGetIntegerv(GL_MAX_LIGHTS, &maxlights);
378    s.sprintf("GL_MAX_LIGHTS==%d\n", maxlights);
379    info += s;
380
381    GLint maxplanes;
382    glGetIntegerv(GL_MAX_CLIP_PLANES, &maxplanes);
383    s.sprintf("GL_MAX_CLIP_PLANES==%d\n", maxplanes);
384    info += s;
385
386    // FIXME: other implementation specifics to print are
387    //
388    //  * maximum stack depths (attribute, modelview matrix, name,
389    //    projection matrix, texture matrix)
390    //
391    //  * max display list nesting
392    //
393    //  * max 3D texture size (needs specific extension?)
394    //
395    // 20020802 mortene.
396  }
397
398  SbString s;
399  s.sprintf("\n"
400            "Rendering is %sdirect.\n",
401            SoGuiGLWidgetP::isDirectRendering(PUBLIC(this)) ? "" : "in");
402  info += s;
403
404  So@Gui@::createSimpleErrorDialog(NULL, "OpenGL driver information",
405                                    info.getString());
406#endif // DEBUGGING_EGGS
407}
408
409void
410So@Gui@RenderAreaP::showInventorInformation(void)
411{
412#if DEBUGGING_EGGS
413  SbString info;
414  info.sprintf("%s\n", SoDB::getVersion());
415
416  // Display calculated maximum resolution of SbTime::getTimeOfDay().
417  {
418    const double DURATION = 0.2;  // in seconds
419    SbTime current = SbTime::getTimeOfDay();
420    SbTime end(current + DURATION);
421    SbTime last = current;
422    unsigned int ticks = 0;
423    do {
424      current = SbTime::getTimeOfDay();
425      if (current.getValue() != last.getValue()) { ticks++; last = current; }
426    } while (current < end);
427    SbString s;
428    s.sprintf("\nSbTime::getTimeOfDay() resolution: ~ %d Hz\n",
429              (int)(((double)ticks) / DURATION));
430    info += s;
431  }
432
433  // FIXME: dump list of available node classes? 20010927 mortene.
434  So@Gui@::createSimpleErrorDialog(NULL, "Inventor implementation info",
435                                    info.getString());
436
437#endif // DEBUGGING_EGGS
438}
439
440void
441So@Gui@RenderAreaP::showToolkitInformation(void)
442{
443#if DEBUGGING_EGGS
444  SbString info = "So@Gui@ version "; info += SO@GUI@_VERSION; info += "\n";
445#if SO@GUI@_MAKE_DLL
446  info += "Built as MSWindows DLL.\n";
447#endif // !SO@GUI@_MAKE_DLL
448
449  // FIXME: include information about the underlying toolkit library,
450  // if possible, like we do for Qt below (ie Gtk version, Motif
451  // implementation, MSWindows version, ...).  20010927 mortene.
452
453#ifdef SOQT_INTERNAL
454  // Qt implementation info.
455  {
456    SbString s;
457    s.sprintf("\nQt version: %s\n", qVersion());
458    info += s;
459  }
460#endif // SOQT_INTERNAL
461
462  // FIXME: information about DLL path(s) (both for the So@Gui@ and
463  // Coin/Inventor library) would be _extremely_ useful for debugging
464  // "remote" applications, as application programmers (including
465  // ourselves) tend to spread those files around misc diskdrive
466  // directories -- especially on MSWindows platforms.  Mismatches for
467  // run-time binding and link-time binding then causes bugs which are
468  // impossible to make sense of.
469  //
470  // I don't know if any platforms have enough introspection
471  // functionality to enable us to do this, though. Should
472  // investigate. (update: GetModuleHandle() looks like the place to
473  // start looking in the Win32 API.)
474  //
475  // 20010927 mortene.
476
477  // OpenGL canvas settings.
478  {
479    SbString s;
480    s.sprintf("\nCurrent OpenGL canvas:\n"
481              "         %sbuffer\n"
482              "         drawing to %sbuffer\n"
483              "         %s rendering%s\n"
484              "         %s mode\n"
485              "         with%s overlay planes\n",
486              PUBLIC(this)->isDoubleBuffer() ? "double" : "single",
487              PUBLIC(this)->isDrawToFrontBufferEnable() ? "front" : "back",
488              PUBLIC(this)->isStereoBuffer() ? "stereo" : "mono",
489              PUBLIC(this)->isQuadBufferStereo() ? " (OpenGL quadbuffer)" : "",
490              PUBLIC(this)->isRGBMode() ? "RGB" : "colorindex",
491              PUBLIC(this)->isOverlayRender() ? "" : "out");
492
493    // FIXME: information about the native OpenGL widget format?
494    // 20010927 mortene.
495
496    info += s;
497  }
498
499  // Underlying Inventor implementation.
500  {
501    SbString s;
502    s.sprintf("\nInventor implementation: %s\n", SoDB::getVersion());
503    info += s;
504  }
505
506  So@Gui@::createSimpleErrorDialog(NULL, "So@Gui@ implementation info",
507                                    info.getString());
508#endif // DEBUGGING_EGGS
509}
510
511void
512So@Gui@RenderAreaP::dumpScenegraph(void)
513{
514#ifdef DEBUGGING_EGGS
515  SoOutput out;
516  SbString filename = SbTime::getTimeOfDay().format();
517  filename += "-dump.iv";
518  SbBool ok = out.openFile(filename.getString());
519  if (!ok) {
520    SoDebugError::post("So@Gui@RenderAreaP::dumpScenegraph",
521                       "couldn't open file '%s'", filename.getString());
522    return;
523  }
524  SoWriteAction wa(&out);
525  wa.apply(this->normalManager->getSceneGraph());
526  SoDebugError::postInfo("So@Gui@RenderAreaP::dumpScenegraph",
527                         "dumped scenegraph to '%s'", filename.getString());
528#endif // DEBUGGING_EGGS
529}
530
531void
532So@Gui@RenderAreaP::dumpCameras(void)
533{
534#ifdef DEBUGGING_EGGS
535  const SbBool kitsearch = SoBaseKit::isSearchingChildren();
536  SoBaseKit::setSearchingChildren(TRUE);
537
538  SoSearchAction search;
539  search.setType(SoCamera::getClassTypeId());
540  search.setInterest(SoSearchAction::ALL);
541  search.setSearchingAll(TRUE);
542  search.apply(this->normalManager->getSceneGraph());
543
544  SoBaseKit::setSearchingChildren(kitsearch);
545
546  const SoPathList & pl = search.getPaths();
547  const unsigned int numcams = pl.getLength();
548  SoDebugError::postInfo("So@Gui@RenderAreaP::dumpCameras",
549                         "Number of cameras in scene graph: %d",
550                         numcams);
551
552  for (unsigned int i = 0; i < numcams; i++) {
553    const SoPath * p = pl[i];
554    SoNode * n = p->getTail();
555    assert(n->isOfType(SoCamera::getClassTypeId()));
556    SoCamera * cam = (SoCamera *)n;
557
558    const SbVec3f pos = cam->position.getValue();
559    const SbRotation rot = cam->orientation.getValue();
560    SbVec3f axis;
561    float angle;
562    rot.getValue(axis, angle);
563
564    SoDebugError::postInfo("So@Gui@RenderAreaP::dumpCameras",
565                           "type==%s, name=='%s', position==<%f, %f, %f>, "
566                           "orientation-rotation==<%f, %f, %f>--%f",
567                           cam->getTypeId().getName().getString(),
568                           cam->getName().getString(),
569                           pos[0], pos[1], pos[2],
570                           axis[0], axis[1], axis[2], angle);
571  }
572#endif // DEBUGGING_EGGS
573}
574
575/*
576  Behaviour controlled by environment variables
577    COIN_SOGRAB_GEOMETRY  (maximum geometry - on-screen aspect is preserved)
578    COIN_SOGRAB_FILENAME  (filename template - can use %d to insert counter)
579
580  Examples:
581    export COIN_SOGRAB_GEOMETRY=1024x768
582    export COIN_SOGRAB_FILENAME=c:\\grab%03d.png
583
584*/
585
586void
587So@Gui@RenderAreaP::offScreenGrab(void)
588{
589#ifdef DEBUGGING_EGGS
590  static int maxwidth = -1;
591  static int maxheight = -1;
592  static int counter = 0;
593  static const char fallback_ext[] = ".rgb";
594  static const char fallback_name[] = "coingrab%03d.rgb";
595
596  /*
597    FIXME:
598
599    - schedule a regular render-pass and hook into the render
600      pipe-line to check all the interesting GL context features that
601      might need to be enabled for the offscreen renderer context.
602      Then set up the offscreen renderer context to match those
603      features, so the rendering becomes the same.
604    - create a clone of the on-screen renderaction to get the same
605      kind of custom rendering.
606    - check if we might be able to hook up the on-screen pre-render
607      callback to the offscreen renderer as well, if any point.
608    - disable accidentally enabled seek mode again (from typing 'osgrab').
609
610    20050606 larsa.
611  */
612
613  counter++;
614  if ( maxwidth <= 0 ) {
615    const char * env =
616      SoAny::si()->getenv("COIN_SOGRAB_GEOMETRY");
617    if ( env ) {
618      sscanf(env, "%dx%d", &maxwidth, &maxheight);
619    }
620    if ( (maxwidth <= 0) || !env ) {
621      SbVec2s vp = PUBLIC(this)->getViewportRegion().getWindowSize();
622      maxwidth = vp[0];
623      maxheight = vp[1];
624    }
625  }
626
627  if ( maxwidth <= 0 || maxheight <= 0 ) {
628    SoDebugError::post("So@Gui@RenderAreaP::offScreenGrab",
629                       "invalid geometry: %dx%d", maxwidth, maxheight);
630    return;
631  }
632  SbVec2s vp = PUBLIC(this)->getViewportRegion().getWindowSize();
633
634  const char * filenametpl =
635    SoAny::si()->getenv("COIN_SOGRAB_FILENAME");
636  if ( !filenametpl ) filenametpl = fallback_name;
637
638  SbString filename;
639  filename.sprintf(filenametpl, counter);
640  const char * ext = strrchr(filename.getString(), '.');
641  if ( !ext ) ext = fallback_ext;
642  ext++;
643
644  SbVec2s osvp(maxwidth, maxheight);
645  if ( vp[0] > maxwidth || vp[1] > maxheight ||
646       (vp[0] < maxwidth && vp[1] < maxheight) ) {
647    float onscaspect = float(vp[0]) / float(vp[1]);
648    float offscaspect = float(maxwidth) / float(maxheight);
649
650    osvp[1] = maxheight;
651    osvp[0] = short(maxheight * onscaspect);
652    if ( osvp[0] > maxwidth ) {
653      osvp[0] = maxwidth;
654      osvp[1] = short(maxwidth * (1.0f / onscaspect));
655    }
656  }
657
658  SoOffscreenRenderer os(osvp);
659  if ( !os.render(PUBLIC(this)->getSceneManager()->getSceneGraph()) ) {
660    return;
661  }
662
663  SbBool written = FALSE;
664  if (strcmp(ext, "rgb") == 0) {
665    written = os.writeToRGB(filename.getString());
666  }
667  else {
668    written = os.writeToFile(filename, ext);
669  }
670
671  if (written) {
672    SoDebugError::postInfo("So@Gui@RenderAreaP::offScreenGrab",
673                           "wrote image #%d, %dx%d as '%s'",
674                           counter, osvp[0], osvp[1],
675                           filename.getString());
676  }
677  else {
678    SoDebugError::post("So@Gui@RenderAreaP::offScreenGrab",
679                       "tried to write image '%s', but failed for unknown "
680                       "reason",
681                       filename.getString());
682  }
683#endif // DEBUGGING_EGGS
684}
685
686int
687So@Gui@RenderAreaP::checkMagicSequences(const char c)
688{
689#if DEBUGGING_EGGS
690  this->currentinput += c;
691
692  if (0) { // handy for debugging keyboard handling
693    SoDebugError::postInfo("So@Gui@RenderAreaP::checkMagicSequences",
694                           "'%s'", this->currentinput.getString());
695  }
696
697  const int cl = this->currentinput.getLength();
698
699  static const char * keyseq[] = {
700    "glinfo", "ivinfo", "soinfo", "dumpiv", "cameras", "osgrab"
701  };
702  static const int id[] = {
703    So@Gui@RenderAreaP::OPENGL,
704    So@Gui@RenderAreaP::INVENTOR,
705    So@Gui@RenderAreaP::TOOLKIT,
706    So@Gui@RenderAreaP::DUMPSCENEGRAPH,
707    So@Gui@RenderAreaP::DUMPCAMERAS,
708    So@Gui@RenderAreaP::OFFSCREENGRAB
709  };
710
711  for (unsigned int i = 0; i < (sizeof(keyseq) / sizeof(keyseq[0])); i++) {
712    const int ml = strlen(keyseq[i]);
713    if (cl >= ml && this->currentinput.getSubString(cl - ml) == keyseq[i]) {
714      return id[i];
715    }
716  }
717
718  // Limit memory usage.
719  if (cl > 1024) { this->currentinput = ""; }
720#endif // DEBUGGING_EGGS
721
722  return So@Gui@RenderAreaP::NONE;
723}
724
725// This method sets the window size data in all the connected device
726// classes.
727void
728So@Gui@RenderAreaP::setDevicesWindowSize(const SbVec2s size)
729{
730  if (!this->devicelist) return;
731  const int num = this->devicelist->getLength();
732  for (int i = 0; i < num; i++)
733    ((So@Gui@Device *)(*this->devicelist)[i])->setWindowSize(size);
734}
735
736// *************************************************************************
737
738void
739So@Gui@RenderAreaP::renderCB(void * closure, SoSceneManager * manager)
740{
741  assert(closure && manager);
742  So@Gui@RenderArea * thisptr = (So@Gui@RenderArea *) closure;
743  if (manager == PRIVATE(thisptr)->normalManager) {
744    thisptr->render();
745  } else if (manager == PRIVATE(thisptr)->overlayManager) {
746    thisptr->renderOverlay();
747  } else {
748#if SO@GUI@_DEBUG
749    SoDebugError::post("So@Gui@RenderAreaP::renderCB",
750                       "invoked for unknown SoSceneManager (%p)", manager);
751#endif // SO@GUI@_DEBUG
752    return;
753  }
754}
755
756// Callback for automatic redraw on SoSelection changes.
757void
758So@Gui@RenderAreaP::selection_redraw_cb(void * closure, SoSelection * sel)
759{
760  So@Gui@RenderArea * ra = (So@Gui@RenderArea *) closure;
761  if (sel == PRIVATE(ra)->normalselection)
762    ra->scheduleRedraw();
763  else if (sel == PRIVATE(ra)->overlayselection)
764    ra->scheduleOverlayRedraw();
765  else
766    assert(0 && "callback on unknown SoSelection node");
767}
768
769// Private class constructor.
770So@Gui@RenderAreaP::So@Gui@RenderAreaP(So@Gui@RenderArea * api)
771{
772  PUBLIC(this) = api;
773
774  this->normalManager = new SoSceneManager;
775  this->overlayManager = new SoSceneManager;
776
777  this->normalColormap = NULL;
778  this->normalColormapSize = 0;
779  this->overlayColormap = NULL;
780  this->overlayColormapSize = 0;
781
782  this->clear = TRUE;
783  this->clearZBuffer = TRUE;
784  this->clearOverlay = TRUE;
785  this->autoRedraw = TRUE;
786
787  this->normalselection = NULL;
788  this->overlayselection = NULL;
789
790  this->devices.mouse = NULL;
791  this->devices.keyboard = NULL;
792#ifdef HAVE_SPACENAV_SUPPORT
793  this->devices.spacenav = NULL;
794#endif // HAVE_SPACENAV_SUPPORT
795
796}
797
798// Private class destructor.
799So@Gui@RenderAreaP::~So@Gui@RenderAreaP()
800{
801  delete this->normalManager;
802  delete this->overlayManager;
803  delete [] this->normalColormap;
804  delete [] this->overlayColormap;
805}
806
807// Common code for all constructors.
808void
809So@Gui@RenderAreaP::constructor(SbBool mouseInput,
810                                SbBool keyboardInput,
811                                SbBool build)
812{
813  this->normalManager->setRenderCallback(So@Gui@RenderAreaP::renderCB, PUBLIC(this));
814  this->normalManager->activate();
815  this->overlayManager->setRenderCallback(So@Gui@RenderAreaP::renderCB, PUBLIC(this));
816  this->overlayManager->activate();
817  // FIXME: what is this magic number doing here - shouldn't we use
818  // SoGLCacheContextElement::getUniqueCacheContext() for Coin, and
819  // magic numbers just for SGI / TGS Inventor?
820  //
821  // On a side note: won't this code fail if we construct several
822  // So@Gui@RenderArea instances with overlays? They will all use
823  // cachecontext==1 for their SoGLRenderAction instances -- is that
824  // kosher?
825  //
826  // 20010831 mortene.
827  this->overlayManager->getGLRenderAction()->setCacheContext(1);
828
829  this->appeventhandler = NULL;
830  this->appeventhandlerdata = NULL;
831
832  this->devicelist = new SbPList;
833
834  if (mouseInput) {
835    this->devices.mouse = new So@Gui@Mouse;
836    PUBLIC(this)->registerDevice(this->devices.mouse);
837  }
838
839  if (keyboardInput) {
840    this->devices.keyboard = new So@Gui@Keyboard;
841    PUBLIC(this)->registerDevice(this->devices.keyboard);
842  }
843
844  if (! build) return;
845  PUBLIC(this)->setClassName("So@Gui@RenderArea");
846  @WIDGET@ glarea = PUBLIC(this)->buildWidget(PUBLIC(this)->getParentWidget());
847  PUBLIC(this)->setBaseWidget(glarea);
848  PUBLIC(this)->setSize(SbVec2s(400, 400));
849}
850
851// This method invokes the application event handler, if one is set.
852SbBool
853So@Gui@RenderAreaP::invokeAppCB(@EVENT@ event)
854{
855  if (this->appeventhandler != NULL)
856    return this->appeventhandler(this->appeventhandlerdata, event);
857  return FALSE;
858}
859
860// This method returns an SoEvent * corresponding to the given \a
861// event, or \c NULL if there are none.
862const SoEvent *
863So@Gui@RenderAreaP::getSoEvent(@EVENT@ event)
864{
865  if (!this->devicelist)
866    return (SoEvent *) NULL;
867
868  const SoEvent * soevent = NULL;
869  const int num = this->devicelist->getLength();
870  for (int i = 0; (i < num) && (soevent == NULL); i++)
871    soevent = ((So@Gui@Device *)(*this->devicelist)[i])->translateEvent(event);
872
873  return soevent;
874}
875
876#endif // DOXYGEN_SKIP_THIS
877
878// *************************************************************************
879
880/*!
881  Public constructor.
882*/
883So@Gui@RenderArea::So@Gui@RenderArea(@WIDGET@ parent,
884                                     const char * name,
885                                     SbBool embed,
886                                     SbBool mouseInput,
887                                     SbBool keyboardInput)
888  : inherited(parent, name, embed, So@Gui@RenderAreaP::GL_DEFAULT_MODE, FALSE)
889{
890  PRIVATE(this) = new So@Gui@RenderAreaP(this);
891  PRIVATE(this)->constructor(mouseInput, keyboardInput, TRUE);
892}
893
894/*!
895  Protected constructor used by derived classes.
896*/
897So@Gui@RenderArea::So@Gui@RenderArea(@WIDGET@ parent,
898                                     const char * name,
899                                     SbBool embed,
900                                     SbBool mouseInput,
901                                     SbBool keyboardInput,
902                                     SbBool build)
903  : inherited(parent, name, embed, So@Gui@RenderAreaP::GL_DEFAULT_MODE, FALSE)
904{
905  PRIVATE(this) = new So@Gui@RenderAreaP(this);
906  PRIVATE(this)->constructor(mouseInput, keyboardInput, build);
907}
908
909/*!
910  Destructor.
911*/
912So@Gui@RenderArea::~So@Gui@RenderArea()
913{
914  // Clean out any callbacks we may have registered with SoSelection
915  // nodes.
916  this->redrawOverlayOnSelectionChange(NULL);
917  this->redrawOnSelectionChange(NULL);
918
919  for (int i = PRIVATE(this)->devicelist->getLength() - 1; i >= 0; i--) {
920    So@Gui@Device * device = (So@Gui@Device *) ((*PRIVATE(this)->devicelist)[i]);
921    this->unregisterDevice(device);
922    delete device;
923  }
924  delete PRIVATE(this)->devicelist;
925  delete PRIVATE(this);
926}
927
928// *************************************************************************
929
930/*!
931  This method adds \a device to the list of devices handling events
932  for this component.
933*/
934void
935So@Gui@RenderArea::registerDevice(So@Gui@Device * device)
936{
937  int idx = PRIVATE(this)->devicelist->find(device);
938  if (idx != -1) {
939#if SO@GUI@_DEBUG
940    SoDebugError::postWarning("So@Gui@RenderArea::registerDevice",
941                              "device already registered");
942#endif // SO@GUI@_DEBUG
943    return;
944  }
945
946  PRIVATE(this)->devicelist->append(device);
947  @WIDGET@ w = this->getGLWidget();
948  if (w != NULL) {
949#ifdef __COIN_SOXT__
950    device->enable(w, (SoXtEventHandler *) &So@Gui@GLWidget::eventHandler, (void *)this);
951#else
952    device->enable(w, &So@Gui@GLWidgetP::eventHandler, (void *)this);
953#endif
954    device->setWindowSize(this->getGLSize());
955  }
956}
957
958/*!
959  This method removes \a device from the list of devices handling
960  events for this component.
961*/
962void
963So@Gui@RenderArea::unregisterDevice(So@Gui@Device * device)
964{
965  assert(PRIVATE(this)->devicelist != NULL);
966  const int idx = PRIVATE(this)->devicelist->find(device);
967  if (idx == -1) {
968#if SO@GUI@_DEBUG
969    SoDebugError::post("So@Gui@RenderArea::unregisterDevice",
970                       "tried to remove nonexisting device");
971#endif // SO@GUI@_DEBUG
972    return;
973  }
974
975  PRIVATE(this)->devicelist->remove(idx);
976  @WIDGET@ w = this->getGLWidget();
977  if (w != NULL) { device->disable(w, NULL, NULL); }
978}
979
980// *************************************************************************
981
982// Documented in superclass.
983void
984So@Gui@RenderArea::afterRealizeHook(void)
985{
986  inherited::afterRealizeHook();
987
988#ifdef HAVE_JOYSTICK_LINUX
989  if (So@Gui@LinuxJoystick::exists())
990    this->registerDevice(new So@Gui@LinuxJoystick);
991#endif // HAVE_JOYSTICK_LINUX
992
993#ifdef HAVE_SPACENAV_SUPPORT
994  PRIVATE(this)->devices.spacenav = new So@Gui@SpacenavDevice;
995  this->registerDevice(PRIVATE(this)->devices.spacenav);
996#endif // HAVE_SPACENAV_SUPPORT
997}
998
999/*!
1000  This method sets the scene graph to be rendered in the normal bitmap
1001  planes.
1002
1003  \sa getSceneGraph(), setOverlaySceneGraph()
1004*/
1005void
1006So@Gui@RenderArea::setSceneGraph(SoNode * scene)
1007{
1008  PRIVATE(this)->normalManager->setSceneGraph(scene);
1009}
1010
1011/*!
1012  This method returns a reference to the scene graph root node as set
1013  by the user.
1014
1015  \sa So@Gui@RenderArea::getSceneManager()
1016*/
1017SoNode *
1018So@Gui@RenderArea::getSceneGraph(void)
1019{
1020  return PRIVATE(this)->normalManager->getSceneGraph();
1021}
1022
1023/*!
1024  This method sets the scene graph to render for the overlay bitmap
1025  planes.
1026
1027  It will automatically take care of setting up overplay planes in the
1028  OpenGL canvas if the OpenGL hardware and driver supports it.
1029
1030  Important note: not all graphics hardware and / or drivers for
1031  graphics hardware support overlay planes, so application programmers
1032  are adviced to find some other way of accomplishing what they want
1033  to do before resorting to using overlay planes.  Using overlay
1034  planes will in practice severely limit the portability of
1035  applications which depend on them being available.
1036
1037  \sa setSceneGraph(), getOverlaySceneGraph()
1038*/
1039void
1040So@Gui@RenderArea::setOverlaySceneGraph(SoNode * scene)
1041{
1042  SoNode * oldroot = this->getOverlaySceneGraph();
1043  PRIVATE(this)->overlayManager->setSceneGraph(scene);
1044
1045  if (!oldroot && scene) { this->setOverlayRender(TRUE); }
1046  else if (oldroot && !scene) { this->setOverlayRender(FALSE); }
1047}
1048
1049/*!
1050  This method returns the scene graph for the overlay scene.
1051*/
1052SoNode *
1053So@Gui@RenderArea::getOverlaySceneGraph(void)
1054{
1055  return PRIVATE(this)->overlayManager->getSceneGraph();
1056}
1057
1058// *************************************************************************
1059
1060/*!
1061  This method sets the background color of the scene.
1062*/
1063void
1064So@Gui@RenderArea::setBackgroundColor(const SbColor & color)
1065{
1066  assert(PRIVATE(this)->normalManager != NULL);
1067  PRIVATE(this)->normalManager->setBackgroundColor(color);
1068  this->scheduleRedraw();
1069}
1070
1071/*!
1072  This method returns the background color for the scene.
1073*/
1074const SbColor &
1075So@Gui@RenderArea::getBackgroundColor(void) const
1076{
1077  assert(PRIVATE(this)->normalManager != NULL);
1078#if HAVE_SBCOLOR4F_GETBACKGROUNDCOLOR
1079  SbColor4f bgcol = PRIVATE(this)->normalManager->getBackgroundColor();
1080  return SbColor(bgcol[0], bgcol[1], bgcol[2]);
1081#else
1082  return PRIVATE(this)->normalManager->getBackgroundColor();
1083#endif
1084}
1085
1086// *************************************************************************
1087
1088/*!
1089  This method sets the index of the background color for the scene.
1090*/
1091void
1092So@Gui@RenderArea::setBackgroundIndex(int idx)
1093{
1094  assert(PRIVATE(this)->normalManager != NULL);
1095  PRIVATE(this)->normalManager->setBackgroundIndex(idx);
1096  this->scheduleRedraw();
1097}
1098
1099/*!
1100  This method returns the index of the background color for the scene.
1101*/
1102int
1103So@Gui@RenderArea::getBackgroundIndex(void) const
1104{
1105  assert(PRIVATE(this)->normalManager != NULL);
1106  return PRIVATE(this)->normalManager->getBackgroundIndex();
1107}
1108
1109// *************************************************************************
1110
1111/*!
1112  This method sets the index of the background for the overlay scene.
1113*/
1114void
1115So@Gui@RenderArea::setOverlayBackgroundIndex(int idx)
1116{
1117  assert(PRIVATE(this)->overlayManager != NULL);
1118  PRIVATE(this)->overlayManager->setBackgroundIndex(idx);
1119  this->scheduleOverlayRedraw();
1120}
1121
1122/*!
1123  This method returns the index of the background for the overlay scene.
1124*/
1125int
1126So@Gui@RenderArea::getOverlayBackgroundIndex(void) const
1127{
1128  assert(PRIVATE(this)->overlayManager != NULL);
1129  return PRIVATE(this)->overlayManager->getBackgroundIndex();
1130}
1131
1132// *************************************************************************
1133
1134/*!
1135  This method sets the colormap for the scene.
1136*/
1137void
1138So@Gui@RenderArea::setColorMap(int start, int num, const SbColor * colors)
1139{
1140  delete [] PRIVATE(this)->normalColormap;
1141  PRIVATE(this)->normalColormapStart = start;
1142  PRIVATE(this)->normalColormapSize = num;
1143  PRIVATE(this)->normalColormap = new SbColor [ num ];
1144  for (int i = 0; i < num; i++)
1145    PRIVATE(this)->normalColormap[i] = colors[i];
1146  this->scheduleRedraw();
1147}
1148
1149/*!
1150  This method sets the colormap for the overlay scene.
1151*/
1152void
1153So@Gui@RenderArea::setOverlayColorMap(int start, int num,
1154                                      const SbColor * colors)
1155{
1156  delete [] PRIVATE(this)->overlayColormap;
1157  PRIVATE(this)->overlayColormapStart = start;
1158  PRIVATE(this)->overlayColormapSize = num;
1159  PRIVATE(this)->overlayColormap = new SbColor [ num ];
1160  for (int i = 0; i < num; i++) {
1161    PRIVATE(this)->overlayColormap[i] = colors[i];
1162  }
1163  this->scheduleOverlayRedraw();
1164}
1165
1166// *************************************************************************
1167
1168/*!
1169  This method sets the viewport region.
1170*/
1171void
1172So@Gui@RenderArea::setViewportRegion(const SbViewportRegion & region)
1173{
1174  if (region.getWindowSize()[0] == -1) return;
1175
1176#if SO@GUI@_DEBUG && 0 // debug
1177  SoDebugError::postInfo("So@Gui@RenderArea::setViewportRegion",
1178                         "size=<%d, %d>",
1179                         region.getWindowSize()[0],
1180                         region.getWindowSize()[1]);
1181#endif // debug
1182
1183  PRIVATE(this)->normalManager->setViewportRegion(region);
1184  PRIVATE(this)->overlayManager->setViewportRegion(region);
1185  this->scheduleRedraw();
1186}
1187
1188/*!
1189  This method returns the viewport region.
1190*/
1191const SbViewportRegion &
1192So@Gui@RenderArea::getViewportRegion(void) const
1193{
1194  assert(PRIVATE(this)->normalManager != NULL);
1195  return PRIVATE(this)->normalManager->getGLRenderAction()->getViewportRegion();
1196}
1197
1198// *************************************************************************
1199
1200/*!
1201  This method sets the transparency type to be used for the scene.
1202*/
1203void
1204So@Gui@RenderArea::setTransparencyType(SoGLRenderAction::TransparencyType type)
1205{
1206  assert(PRIVATE(this)->normalManager != NULL);
1207  PRIVATE(this)->normalManager->getGLRenderAction()->setTransparencyType(type);
1208  PRIVATE(this)->overlayManager->getGLRenderAction()->setTransparencyType(type);
1209  this->scheduleRedraw();
1210}
1211
1212/*!
1213  This method returns the transparency type used for the scene.
1214*/
1215SoGLRenderAction::TransparencyType
1216So@Gui@RenderArea::getTransparencyType(void) const
1217{
1218  assert(PRIVATE(this)->normalManager != NULL);
1219  return PRIVATE(this)->normalManager->getGLRenderAction()->getTransparencyType();
1220}
1221
1222// *************************************************************************
1223
1224/*!
1225  This method sets the antialiasing used for the scene.
1226
1227  The \a smoothing flag signifies whether or not line and point
1228  aliasing should be turned on. See documentation of
1229  SoGLRenderAction::setSmoothing(), which will be called from this
1230  function.
1231
1232  \a numPasses gives the number of re-renderings to do of the scene,
1233  blending together the results from slight "jitters" of the camera
1234  view, into the OpenGL accumulation buffer. For further information,
1235  see documentation of SoGLRenderAction::setNumPasses() and
1236  So@Gui@GLWidget::setAccumulationBuffer().
1237*/
1238void
1239So@Gui@RenderArea::setAntialiasing(SbBool smoothing, int numPasses)
1240{
1241  // FIXME: is this really necessary? I think we should either ignore
1242  // the call or store values for later migration if the scenemanager
1243  // instance(s) haven't been made yet. 20010331 mortene.
1244  assert(PRIVATE(this)->normalManager != NULL);
1245
1246  // Instead of piping the call further to
1247  // SoSceneManager::setAntialiasing(), we duplicate the code found in
1248  // that function. The reason for this is that we want to work around
1249  // a bug found in SGI Inventor, where they define the
1250  // setAntialiasing() method, but doesn't actually implement it.  So
1251  // we don't use it to avoid a linker error for those compiling So*
1252  // libraries on top of the older SGI Inventor versions with this
1253  // bug.
1254  //
1255  // We should perhaps throw in a configure check for the
1256  // SoSceneManager::setAntialiasing() method and only activate this
1257  // code when actually needed?
1258  //                                                   mortene@sim.no
1259  enum { MGRS = 2 };
1260  SoSceneManager * mgrs[MGRS] = { PRIVATE(this)->normalManager,
1261                                  PRIVATE(this)->overlayManager };
1262  for (int i=0; i < MGRS; i++) {
1263    SoGLRenderAction * glra = mgrs[i]->getGLRenderAction();
1264    if (glra) {
1265      glra->setSmoothing(smoothing);
1266      glra->setNumPasses(numPasses);
1267    }
1268  }
1269
1270  this->scheduleRedraw();
1271}
1272
1273/*!
1274  This method returns the antialiasing used for the scene.
1275*/
1276void
1277So@Gui@RenderArea::getAntialiasing(SbBool & smoothing, int & numPasses) const
1278{
1279  // FIXME: there's an API design flaw here, as it is assumed that the
1280  // antialiasing setting for the renderaction in the "normal"
1281  // rendering context always matches what is the case for the
1282  // renderaction in the overlay manager. This is not necessarily
1283  // true. Could be solved by an additional argument to
1284  // getAntialiasing(): a boolean indicator on whether or not we want
1285  // the overlay context with a default value (false) to keep API
1286  // compatibility. 20010331 mortene.
1287
1288  assert(PRIVATE(this)->normalManager != NULL);
1289
1290  // About why we don't use SoSceneManager::getAntialiasing()
1291  // directly, see comment in SoGuiRenderArea::setAntiAliasing().
1292  SoGLRenderAction * glra = PRIVATE(this)->normalManager->getGLRenderAction();
1293  smoothing = glra->isSmoothing();
1294  numPasses = glra->getNumPasses();
1295}
1296
1297/*!
1298  This method sets whether the render buffer should be cleared before
1299  rendering.
1300
1301  The first argument specifies whether or not to clear out the pixels
1302  in the buffer, the second argument specifies whether or not the
1303  z-buffer values should be cleared between renderings.
1304
1305  Setting the first argument to \c FALSE can for instance be used when
1306  you want to clear out the buffer yourself, for instance by drawing a
1307  background image "under" the 3D scene rendered by Coin / Inventor.
1308*/
1309void
1310So@Gui@RenderArea::setClearBeforeRender(SbBool enable, SbBool zbEnable)
1311{
1312  PRIVATE(this)->clear = enable;
1313  PRIVATE(this)->clearZBuffer = zbEnable;
1314
1315  this->scheduleRedraw();
1316}
1317
1318/*!
1319  This method returns whether the render buffer is cleared before each
1320  render.
1321*/
1322SbBool
1323So@Gui@RenderArea::isClearBeforeRender(void) const
1324{
1325  return PRIVATE(this)->clear;
1326}
1327
1328/*!
1329  This method returns whether the render buffer's Z buffer is cleared
1330  before each render.
1331*/
1332SbBool
1333So@Gui@RenderArea::isClearZBufferBeforeRender(void) const
1334{
1335  return PRIVATE(this)->clearZBuffer;
1336}
1337
1338/*!
1339  This method sets whether the overlay render buffer should be cleared
1340  before each render or not.
1341*/
1342void
1343So@Gui@RenderArea::setClearBeforeOverlayRender(SbBool enable)
1344{
1345  PRIVATE(this)->clearOverlay = enable;
1346  this->scheduleOverlayRedraw();
1347}
1348
1349/*!
1350  This method returns whether the overlay render buffer is cleared
1351  before each redraw or not.
1352*/
1353SbBool
1354So@Gui@RenderArea::isClearBeforeOverlayRender(void) const
1355{
1356  return PRIVATE(this)->clearOverlay;
1357}
1358
1359/*!
1360  This method sets whether redrawing should be handled automatically
1361  or not when data in the scenegraph changes.
1362
1363  The default setting causes the renderarea to automatically trigger a
1364  redraw of the scenegraph contents.
1365*/
1366void
1367So@Gui@RenderArea::setAutoRedraw(SbBool enable)
1368{
1369  if (enable) {
1370    PRIVATE(this)->normalManager->activate();
1371    PRIVATE(this)->overlayManager->activate();
1372  }
1373  else {
1374    PRIVATE(this)->normalManager->deactivate();
1375    PRIVATE(this)->overlayManager->deactivate();
1376  }
1377  PRIVATE(this)->autoRedraw = enable;
1378}
1379
1380/*!
1381  This method returns whether redrawing is handled automatically
1382  not.
1383*/
1384SbBool
1385So@Gui@RenderArea::isAutoRedraw(void) const
1386{
1387  return PRIVATE(this)->autoRedraw;
1388}
1389
1390/*!
1391  This method sets the redraw priority.
1392*/
1393void
1394So@Gui@RenderArea::setRedrawPriority(uint32_t priority)
1395{
1396  PRIVATE(this)->normalManager->setRedrawPriority(priority);
1397  PRIVATE(this)->overlayManager->setRedrawPriority(priority);
1398}
1399
1400/*!
1401  This method returns the redraw priority.
1402*/
1403uint32_t
1404So@Gui@RenderArea::getRedrawPriority(void) const
1405{
1406  assert(PRIVATE(this)->normalManager != NULL);
1407  return PRIVATE(this)->normalManager->getRedrawPriority();
1408}
1409
1410/*!
1411  This function returns the default redraw priority.
1412*/
1413uint32_t
1414So@Gui@RenderArea::getDefaultRedrawPriority(void)
1415{
1416  return SoSceneManager::getDefaultRedrawPriority();
1417}
1418
1419/*!
1420  This method causes the immediate rendering of the scene, by calling
1421  So@Gui@RenderArea::redraw().
1422*/
1423void
1424So@Gui@RenderArea::render(void)
1425{
1426  this->redraw();
1427}
1428
1429/*!
1430  This method renders the overlay scene.
1431*/
1432void
1433So@Gui@RenderArea::renderOverlay(void)
1434{
1435  this->redrawOverlay();
1436}
1437
1438/*!
1439  This method schedules a redraw to happen at a later time (when the
1440  application has processed it's other events first).
1441*/
1442void
1443So@Gui@RenderArea::scheduleRedraw(void)
1444{
1445#if SO@GUI@_DEBUG && RENDERAREA_DEBUG_REDRAWS // debug
1446  SoDebugError::postInfo("So@Gui@RenderArea::scheduleRedraw",
1447                         "invoked");
1448#endif // debug
1449
1450  assert(PRIVATE(this)->normalManager != NULL);
1451  PRIVATE(this)->normalManager->scheduleRedraw(); // Redraw when idle.
1452}
1453
1454/*!
1455  This method schedules a redraw of the overlay scene.
1456*/
1457void
1458So@Gui@RenderArea::scheduleOverlayRedraw(void)
1459{
1460  assert(PRIVATE(this)->overlayManager != NULL);
1461  PRIVATE(this)->overlayManager->scheduleRedraw(); // Redraw when idle.
1462}
1463
1464#ifndef DOXYGEN_SKIP_THIS
1465void
1466So@Gui@RenderAreaP::replaceSoSelectionMonitor(SoSelection * newsel,
1467                                              SoSelection * oldsel) const
1468{
1469  if (newsel) { newsel->ref(); }
1470
1471  if (oldsel) {
1472    oldsel->removeChangeCallback(So@Gui@RenderAreaP::selection_redraw_cb,
1473                                 PUBLIC(this));
1474    oldsel->unref();
1475  }
1476
1477  if (newsel) {
1478    newsel->addChangeCallback(So@Gui@RenderAreaP::selection_redraw_cb,
1479                              PUBLIC(this));
1480  }
1481}
1482#endif // DOXYGEN_SKIP_THIS
1483
1484// FIXME: the interface below is badly designed, see the comment in
1485// the function documentation. 20001002 mortene.
1486
1487/*!
1488  Do automatic redraw of the scenegraph when a selection under the
1489  SoSelection node is changed.
1490
1491  Pass \c NULL to deactivate.
1492
1493  (Only one SoSelection node can be monitored at any given time. This
1494  is obviously a rather silly design flaw. We choose to match the
1495  original Inventor API here, but this will probably change in the
1496  next major revision of the library.)
1497*/
1498void
1499So@Gui@RenderArea::redrawOnSelectionChange(SoSelection * selection)
1500{
1501  PRIVATE(this)->replaceSoSelectionMonitor(selection, PRIVATE(this)->normalselection);
1502  PRIVATE(this)->normalselection = selection;
1503}
1504
1505/*!
1506  Do automatic redraw of the scenegraph in the overlay planes when a
1507  selection under the SoSelection node is changed.
1508
1509  Pass \c NULL to deactivate.
1510
1511  \sa So@Gui@RenderArea::redrawOnSelectionChange()
1512*/
1513void
1514So@Gui@RenderArea::redrawOverlayOnSelectionChange(SoSelection * selection)
1515{
1516  PRIVATE(this)->replaceSoSelectionMonitor(selection, PRIVATE(this)->overlayselection);
1517  PRIVATE(this)->overlayselection = selection;
1518}
1519
1520/*!
1521  This method sets the render area event callback.
1522*/
1523void
1524So@Gui@RenderArea::setEventCallback(So@Gui@RenderAreaEventCB * func,
1525                                    void * user)
1526{
1527  PRIVATE(this)->appeventhandler = func;
1528  PRIVATE(this)->appeventhandlerdata = user;
1529}
1530
1531/*!
1532  This method sets the normal scene SoSceneManager object.
1533
1534  The previous set scene manager is deleted, and there is no way to
1535  currently avoid that.  This might change in the future.
1536*/
1537void
1538So@Gui@RenderArea::setSceneManager(SoSceneManager * manager)
1539{
1540  assert(PRIVATE(this)->normalManager != NULL);
1541  PRIVATE(this)->normalManager->setRenderCallback(NULL, NULL);
1542
1543  // NOTE: Although deleting the previous scene manager here is correct
1544  // behaviour from a compatibility-POV, I think it is ugly and should
1545  // be addressed in some other way if possible. It is also inconsistent
1546  // with overlay scenemanager management.  20061015 larsa
1547  delete PRIVATE(this)->normalManager;
1548
1549  PRIVATE(this)->normalManager = manager;
1550  if (PRIVATE(this)->normalManager) {
1551    PRIVATE(this)->normalManager->setSize(this->getGLSize());
1552  }
1553}
1554
1555/*!
1556  This method returns the normal scene SoSceneManager object.
1557
1558  Having a reference to the SoSceneManager instance is useful for
1559  getting at the \e real root node of the rendering scenegraph,
1560  including camera, headlight and miscellaneous drawstyle nodes.  The
1561  getSceneGraph() method will only return the \e user scenegrah for
1562  So@Gui@RenderArea subclass So@Gui@Viewer and further subclasses. The
1563  reason this is not always what you want is because certain actions
1564  (like the SoRayPickAction) needs to traverse a valid camera if it
1565  should work as expected.
1566
1567  If you need to get a pointer to the \e real root node use this
1568  method to get the SoSceneManager instance reference used by the
1569  So@Gui@RenderArea, then use SoSceneManager::getSceneGraph() to get
1570  the root node Coin uses for rendering.
1571*/
1572SoSceneManager *
1573So@Gui@RenderArea::getSceneManager(void) const
1574{
1575  return PRIVATE(this)->normalManager;
1576}
1577
1578/*!
1579  This method sets the overlay scene SoSceneManager object.
1580
1581  The previous set scene manager is not freed and will leak unless
1582  the user frees it.
1583*/
1584void
1585So@Gui@RenderArea::setOverlaySceneManager(SoSceneManager * manager)
1586{
1587  PRIVATE(this)->overlayManager = manager;
1588  if (PRIVATE(this)->overlayManager) {
1589    PRIVATE(this)->overlayManager->setSize(this->getGLSize());
1590  }
1591}
1592
1593/*!
1594  This method returns the overlay scene SoSceneManager object.
1595*/
1596SoSceneManager *
1597So@Gui@RenderArea::getOverlaySceneManager(void) const
1598{
1599  return PRIVATE(this)->overlayManager;
1600}
1601
1602/*!
1603  This method sets the SoGLRenderAction object for the normal scene.
1604*/
1605void
1606So@Gui@RenderArea::setGLRenderAction(SoGLRenderAction * action)
1607{
1608  assert(PRIVATE(this)->normalManager != NULL);
1609  PRIVATE(this)->normalManager->setGLRenderAction(action);
1610  // Force an update of the SoGLRenderAction to the correct
1611  // updatearea, aspectratio, etc.
1612  this->sizeChanged(this->getSize());
1613}
1614
1615/*!
1616  This method returns the SoGLRenderAction object for the normal scene.
1617*/
1618SoGLRenderAction *
1619So@Gui@RenderArea::getGLRenderAction(void) const
1620{
1621  assert(PRIVATE(this)->normalManager != NULL);
1622  return PRIVATE(this)->normalManager->getGLRenderAction();
1623}
1624
1625/*!
1626  This method sets the SoGLRenderAction object for rendering the
1627  overlay scenegraph.
1628*/
1629void
1630So@Gui@RenderArea::setOverlayGLRenderAction(SoGLRenderAction * action)
1631{
1632  assert(PRIVATE(this)->overlayManager != NULL);
1633  PRIVATE(this)->overlayManager->setGLRenderAction(action);
1634}
1635
1636/*!
1637  This method returns the SoGLRenderAction object for the overlay scene
1638  graph.
1639*/
1640SoGLRenderAction *
1641So@Gui@RenderArea::getOverlayGLRenderAction(void) const
1642{
1643  assert(PRIVATE(this)->overlayManager != NULL);
1644  return PRIVATE(this)->overlayManager->getGLRenderAction();
1645}
1646
1647/*!
1648  This method is called from the render() method and takes care of
1649  setting up the context for OpenGL rendering (by making the OpenGL
1650  canvas the current context and specifying either the front or back
1651  buffer for rendering, depending on whether we're in singlebuffer or
1652  doublebuffer mode).
1653
1654  After setting up the OpenGL context, it calls actualRedraw() for the
1655  actual scenegraph rendering to take place.
1656
1657  Finally, the OpenGL buffers are either swapped back-to-front (for
1658  doublebuffering) or flushed (for singlebuffering), and our OpenGL
1659  context is unlocked.
1660
1661  The application programmer may override this method if extreme
1662  low-level control of the rendering process is necessary. Usually,
1663  you should be able to get away with overriding actualRedraw() for
1664  special cases, though.
1665*/
1666void
1667So@Gui@RenderArea::redraw(void)
1668{
1669#if SO@GUI@_DEBUG && RENDERAREA_DEBUG_REDRAWS // debug
1670  SoDebugError::postInfo("So@Gui@RenderArea::redraw",
1671                         "start (isVisible=%s waitForExpose=%s)",
1672                         this->isVisible() ? "TRUE" : "FALSE",
1673                         this->waitForExpose ? "TRUE" : "FALSE");
1674#endif // debug
1675
1676  if (! this->isVisible() || !this->hasNormalGLArea() || this->waitForExpose)
1677    return;
1678  this->glLockNormal(); // this makes the GL context "current"
1679
1680  SbBool drawfront =
1681    ! this->isDoubleBuffer() ||
1682    this->isDrawToFrontBufferEnable();
1683
1684  glDrawBuffer(drawfront ? GL_FRONT : GL_BACK);
1685
1686  this->actualRedraw();
1687
1688  if (!drawfront) { this->glSwapBuffers(); }
1689  else { this->glFlushBuffer(); }
1690  this->glUnlockNormal();
1691
1692#if SO@GUI@_DEBUG && RENDERAREA_DEBUG_REDRAWS // debug
1693  SoDebugError::postInfo("So@Gui@RenderArea::render", "done");
1694#endif // debug
1695}
1696
1697// Note: the following function documentation block will also be used
1698// for all the miscellaneous viewer subclasses, so keep it general.
1699/*!
1700  This method instantly redraws the normal (non-overlay) scenegraph by
1701  calling SoSceneManager::render().
1702
1703  Subclasses may override this method to add their own rendering
1704  before or after Coin renders it's scenegraph.
1705
1706  The following is a complete example that demonstrates one way of
1707  adding both a background image and foreground (overlay) geometry to
1708  the "normal" rendering:
1709
1710  \code
1711  // This example shows how to put a permanent background image on
1712  // your viewer canvas, below the 3D graphics, plus overlay
1713  // foreground geometry.  Written by mortene.
1714  // Copyright Kongsberg Oil & Gas Technologies 2002.
1715
1716  // *************************************************************************
1717
1718  #include <Inventor/@Gui@/So@Gui@.h>
1719  #include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h>
1720  #include <Inventor/nodes/SoBaseColor.h>
1721  #include <Inventor/nodes/SoCone.h>
1722  #include <Inventor/nodes/SoCube.h>
1723  #include <Inventor/nodes/SoImage.h>
1724  #include <Inventor/nodes/SoLightModel.h>
1725  #include <Inventor/nodes/SoOrthographicCamera.h>
1726  #include <Inventor/nodes/SoRotationXYZ.h>
1727  #include <Inventor/nodes/SoSeparator.h>
1728  #include <Inventor/nodes/SoTranslation.h>
1729
1730  #include <GL/gl.h>
1731
1732  // *************************************************************************
1733
1734  class MyExaminerViewer : public So@Gui@ExaminerViewer {
1735
1736  public:
1737    MyExaminerViewer(@WIDGET@ parent, const char * filename);
1738    ~MyExaminerViewer();
1739
1740  protected:
1741    virtual void actualRedraw(void);
1742
1743  private:
1744    SoSeparator * bckgroundroot;
1745    SoSeparator * foregroundroot;
1746    SoRotationXYZ * arrowrotation;
1747  };
1748
1749  MyExaminerViewer::MyExaminerViewer(@WIDGET@ parent, const char * filename)
1750    : So@Gui@ExaminerViewer(parent)
1751  {
1752    // Coin should not clear the pixel-buffer, so the background image
1753    // is not removed.
1754    this->setClearBeforeRender(FALSE, TRUE);
1755
1756
1757    // Set up background scenegraph with image in it.
1758
1759    this->bckgroundroot = new SoSeparator;
1760    this->bckgroundroot->ref();
1761
1762    SoOrthographicCamera * cam = new SoOrthographicCamera;
1763    cam->position = SbVec3f(0, 0, 1);
1764    cam->height = 1;
1765    // SoImage will be at z==0.0.
1766    cam->nearDistance = 0.5;
1767    cam->farDistance = 1.5;
1768
1769    SoImage * img = new SoImage;
1770    img->vertAlignment = SoImage::HALF;
1771    img->horAlignment = SoImage::CENTER;
1772    img->filename = filename;
1773
1774    this->bckgroundroot->addChild(cam);
1775    this->bckgroundroot->addChild(img);
1776
1777    // Set up foreground, overlayed scenegraph.
1778
1779    this->foregroundroot = new SoSeparator;
1780    this->foregroundroot->ref();
1781
1782    SoLightModel * lm = new SoLightModel;
1783    lm->model = SoLightModel::BASE_COLOR;
1784
1785    SoBaseColor * bc = new SoBaseColor;
1786    bc->rgb = SbColor(1, 1, 0);
1787
1788    cam = new SoOrthographicCamera;
1789    cam->position = SbVec3f(0, 0, 5);
1790    cam->height = 10;
1791    cam->nearDistance = 0;
1792    cam->farDistance = 10;
1793
1794    const double ARROWSIZE = 2.0;
1795
1796    SoTranslation * posit = new SoTranslation;
1797    posit->translation = SbVec3f(-2.5 * ARROWSIZE, 1.5 * ARROWSIZE, 0);
1798
1799    arrowrotation = new SoRotationXYZ;
1800    arrowrotation->axis = SoRotationXYZ::Z;
1801
1802    SoTranslation * offset = new SoTranslation;
1803    offset->translation = SbVec3f(ARROWSIZE/2.0, 0, 0);
1804
1805    SoCube * cube = new SoCube;
1806    cube->width = ARROWSIZE;
1807    cube->height = ARROWSIZE/15.0;
1808
1809    this->foregroundroot->addChild(cam);
1810    this->foregroundroot->addChild(lm);
1811    this->foregroundroot->addChild(bc);
1812    this->foregroundroot->addChild(posit);
1813    this->foregroundroot->addChild(arrowrotation);
1814    this->foregroundroot->addChild(offset);
1815    this->foregroundroot->addChild(cube);
1816  }
1817
1818  MyExaminerViewer::~MyExaminerViewer()
1819  {
1820    this->bckgroundroot->unref();
1821    this->foregroundroot->unref();
1822  }
1823
1824  void
1825  MyExaminerViewer::actualRedraw(void)
1826  {
1827    // Must set up the OpenGL viewport manually, as upon resize
1828    // operations, Coin won't set it up until the SoGLRenderAction is
1829    // applied again. And since we need to do glClear() before applying
1830    // the action..
1831    const SbViewportRegion vp = this->getViewportRegion();
1832    SbVec2s origin = vp.getViewportOriginPixels();
1833    SbVec2s size = vp.getViewportSizePixels();
1834    glViewport(origin[0], origin[1], size[0], size[1]);
1835
1836    const SbColor col = this->getBackgroundColor();
1837    glClearColor(col[0], col[1], col[2], 0.0f);
1838    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1839
1840    // Render our scenegraph with the image.
1841    SoGLRenderAction * glra = this->getGLRenderAction();
1842    glra->apply(this->bckgroundroot);
1843
1844
1845    // Render normal scenegraph.
1846    So@Gui@ExaminerViewer::actualRedraw();
1847
1848
1849    // Increase arrow angle with 1/1000 � every frame.
1850    arrowrotation->angle = arrowrotation->angle.getValue() + (0.001 / M_PI * 180);
1851    // Render overlay front scenegraph.
1852    glClear(GL_DEPTH_BUFFER_BIT);
1853    glra->apply(this->foregroundroot);
1854  }
1855
1856  // *************************************************************************
1857
1858  int
1859  main(int argc, char ** argv)
1860  {
1861    if (argc != 2) {
1862      (void)fprintf(stderr, "\n\n\tUsage: %s <image-filename>\n\n", argv[0]);
1863      exit(1);
1864    }
1865
1866    @WIDGET@ window = So@Gui@::init(argv[0]);
1867
1868    MyExaminerViewer * viewer = new MyExaminerViewer(window, argv[1]);
1869
1870    viewer->setSceneGraph(new SoCone);
1871    viewer->show();
1872
1873    So@Gui@::show(window);
1874    So@Gui@::mainLoop();
1875
1876    delete viewer;
1877    return 0;
1878  }
1879
1880  // *************************************************************************
1881  \endcode
1882*/
1883void
1884So@Gui@RenderArea::actualRedraw(void)
1885{
1886  assert(PRIVATE(this)->normalManager != NULL);
1887  if ( !this->isVisible() ) return;
1888  PRIVATE(this)->normalManager->render(PRIVATE(this)->clear, PRIVATE(this)->clearZBuffer);
1889}
1890
1891/*!
1892  This method redraws the overlay scene.
1893*/
1894void
1895So@Gui@RenderArea::redrawOverlay(void)
1896{
1897  if (!this->isVisible() || this->waitForExpose || !this->hasOverlayGLArea()) {
1898    return;
1899  }
1900
1901  this->glLockOverlay();
1902  this->actualOverlayRedraw();
1903  this->glFlushBuffer();
1904  this->glUnlockOverlay();
1905}
1906
1907/*!
1908  This method renders the overlay scene.
1909*/
1910void
1911So@Gui@RenderArea::actualOverlayRedraw(void)
1912{
1913  assert(PRIVATE(this)->overlayManager != NULL);
1914  if (! this->isVisible()) return;
1915  PRIVATE(this)->overlayManager->render(PRIVATE(this)->clearOverlay,
1916                                        PRIVATE(this)->clearZBuffer);
1917}
1918
1919/*!
1920  This method is invoked to initialize the normal graphics.
1921*/
1922void
1923So@Gui@RenderArea::initGraphic(void)
1924{
1925  SoSceneManager * mgr = this->getSceneManager();
1926  if (mgr) {
1927    mgr->reinitialize();
1928    mgr->setRGBMode(this->isRGBMode());
1929
1930    SoGLRenderAction * renderaction = mgr->getGLRenderAction();
1931    renderaction->setCacheContext(SoAny::si()->getSharedCacheContextId(this));
1932    SbBool isdirect = SoGuiGLWidgetP::isDirectRendering(this);
1933    renderaction->setRenderingIsRemote(!isdirect);
1934  }
1935  // FIXME: if not RGB mode, load colormap. pederb.
1936
1937  inherited::initGraphic();
1938}
1939
1940/*!
1941  This method is invoked to initialize the overlay graphics.
1942*/
1943void
1944So@Gui@RenderArea::initOverlayGraphic(void)
1945{
1946  SoSceneManager * mgr = this->getOverlaySceneManager();
1947  if (mgr) {
1948    mgr->reinitialize();
1949    // FIXME: does overlays really _have_ to be in colorindex mode?
1950    // 20020916 mortene.
1951    mgr->setRGBMode(FALSE);
1952
1953    SoGLRenderAction * renderaction = mgr->getGLRenderAction();
1954    SbBool isdirect = SoGuiGLWidgetP::isDirectRendering(this);
1955    renderaction->setRenderingIsRemote(!isdirect);
1956    // FIXME: shouldn't we also setCacheContext() on the renderaction?
1957    // 20020916 mortene.
1958  }
1959  // FIXME: if not RGB mode, load colormap. pederb.
1960
1961  // FIXME: shouldn't we do inherited::initOverlayGraphic() ? 20010831 mortene.
1962}
1963
1964// doc in super
1965void
1966So@Gui@RenderArea::sizeChanged(const SbVec2s & size)
1967{
1968#if SO@GUI@_DEBUG && 0
1969  SoDebugError::postInfo("So@Gui@RenderArea::sizeChanged",
1970                          "invoked, <%d, %d>", size[0], size[1]);
1971#endif // SO@GUI@_DEBUG
1972
1973  SbVec2s newsize(size);
1974
1975  if (size[0] == -1)
1976    return;
1977
1978  assert(PRIVATE(this)->normalManager != NULL);
1979  assert(PRIVATE(this)->overlayManager != NULL);
1980
1981  // Workaround for a bug in Qt/Mac 3.1.0 and 3.1.1 (which has been
1982  // confirmed fixed in 3.1.2):
1983  //
1984  // If the OpenGL context overlaps with the QSizeGrip widget
1985  // (generated by default), resizing does not work any more. The
1986  // workaround is to leave 15 pixels at the lower border of the
1987  // window blank...
1988
1989#if defined Q_OS_MAC && ((QT_VERSION == 0x030100) || (QT_VERSION == 0x030101))
1990
1991  // Environment variable to override Qt/Mac 3.1.x workarounds.
1992  const char * forcenoresizeworkaround =
1993    SoAny::si()->getenv("FORCE_NO_QTMAC_31_RESIZE_WORKAROUND");
1994  if (!forcenoresizeworkaround || (atoi(forcenoresizeworkaround) == 0)) {
1995
1996    if (this->getTypeId() == So@Gui@RenderArea::getClassTypeId()) {
1997      // SoQtRenderArea used as standalone component
1998      newsize -= SbVec2s(0, 15);
1999
2000      // spit out a warning that this is a Qt/Mac bug, not an SoQt problem
2001      const char * env = SoAny::si()->getenv("SOQT_NO_QTMAC_BUG_WARNINGS");
2002      if (!env || !atoi(env)) {
2003        SoDebugError::postWarning("SoQtRenderArea::sizeChanged",
2004                                  "\nThis version of Qt/Mac contains a bug "
2005                                  "that makes it necessary to leave the\n"
2006                                  "lowermost 15 pixels of the viewer window "
2007                                  "blank. Set the environment variable\n"
2008                                  "FORCE_NO_QTMAC_31_RESIZE_WORKAROUND=1 to "
2009                                  "override this workaround. \n"
2010                                  "You can turn off warnings about Qt/Mac "
2011                                  "bugs permanently by setting \n"
2012                                  "SOQT_NO_QTMAC_BUG_WARNINGS=1.\n");
2013      }
2014    }
2015  }
2016
2017#endif
2018
2019  this->setGLSize(newsize);
2020  const SbVec2s glsize = this->getGLSize();
2021
2022
2023#if SO@GUI@_DEBUG && 0
2024  SoDebugError::postInfo("So@Gui@RenderArea::sizeChanged",
2025                          "glsize==<%d, %d>", glsize[0], glsize[1]);
2026#endif // SO@GUI@_DEBUG
2027
2028  if (glsize[0] <= 0 || glsize[1] <= 0)
2029    return;
2030
2031  this->setViewportRegion(SbViewportRegion(glsize));
2032  PRIVATE(this)->setDevicesWindowSize(glsize);
2033
2034  // FIXME: aren't both these calls unnecessary as we set the full
2035  // viewportregion a few lines above this one? 20020103 mortene.
2036  PRIVATE(this)->normalManager->setWindowSize(glsize);
2037  PRIVATE(this)->normalManager->setSize(glsize);
2038
2039  // FIXME: aren't both these calls unnecessary as we set the full
2040  // viewportregion a few lines above this one? 20020103 mortene.
2041  PRIVATE(this)->overlayManager->setWindowSize(glsize);
2042  PRIVATE(this)->overlayManager->setSize(glsize);
2043
2044  inherited::sizeChanged(glsize);
2045
2046  // this->scheduleRedraw(); // already done through setViewportRegion()
2047}
2048
2049// Documented in superclass.
2050void
2051So@Gui@RenderArea::widgetChanged(@WIDGET@ widget)
2052{
2053  PRIVATE(this)->normalManager->reinitialize();
2054  PRIVATE(this)->overlayManager->reinitialize();
2055  // FIXME: colorindex mode not supported yet, so this has no
2056  // effect. 20001121 mortene.
2057#if 0
2058  PRIVATE(this)->normalManager->setRGBMode(this->isRGBMode());
2059  PRIVATE(this)->overlayManager->setRGBMode(this->isRGBMode());
2060#endif
2061
2062  // FIXME: should also walk through all registered devices and do a
2063  // disable() on the old widget and enable() on the new one.
2064  // 20001121 mortene.
2065}
2066
2067// Documented in superclass.
2068@WIDGET@
2069So@Gui@RenderArea::buildWidget(@WIDGET@ parent)
2070{
2071  @WIDGET@ widget = inherited::buildWidget(parent);
2072
2073  if (PRIVATE(this)->devicelist != NULL) {
2074    const int num = PRIVATE(this)->devicelist->getLength();
2075    for (int i = 0; i < num; i++) {
2076      So@Gui@Device * device = (So@Gui@Device *) (*PRIVATE(this)->devicelist)[i];
2077#ifdef __COIN_SOXT__
2078      device->enable(this->getGLWidget(),
2079                     (SoXtEventHandler *) &So@Gui@GLWidget::eventHandler,
2080                     (void *) this);
2081#else
2082      device->enable(this->getGLWidget(),
2083                     &So@Gui@GLWidgetP::eventHandler, (void *) this);
2084#endif
2085    }
2086  }
2087
2088  return widget;
2089}
2090
2091// Documented in superclass.
2092const char *
2093So@Gui@RenderArea::getDefaultWidgetName(void) const
2094{
2095  static const char defaultWidgetName[] = "So@Gui@WidgetName";
2096  return defaultWidgetName;
2097}
2098
2099// Documented in superclass.
2100const char *
2101So@Gui@RenderArea::getDefaultTitle(void) const
2102{
2103  static const char defaultTitle[] = "@Gui@ RenderArea";
2104  return defaultTitle;
2105}
2106
2107// Documented in superclass.
2108const char *
2109So@Gui@RenderArea::getDefaultIconTitle(void) const
2110{
2111  static const char defaultIconTitle[] = "@Gui@ RenderArea";
2112  return defaultIconTitle;
2113}
2114
2115// *************************************************************************
2116
2117/*!
2118  Toolkit-native events are attempted converted to Coin-generic events
2119  in the So@Gui@RenderArea::processEvent() method.  If this succeeds,
2120  they are forwarded to this method.
2121
2122  This is a virtual method, and is overridden in it's subclasses to
2123  catch events of particular interest to the viewer classes, for
2124  instance.
2125
2126  Return \c TRUE iff the event was processed. If not it should be
2127  passed on further up in the inheritance hierarchy by the caller.
2128  This last point is extremely important to take note of if you are
2129  expanding the toolkit with your own viewer class.
2130
2131  This method is not part of the original SGI InventorXt API. Note
2132  that you can still override the toolkit-native processEvent() method
2133  instead of this "generic" method.
2134*/
2135SbBool
2136So@Gui@RenderArea::processSoEvent(const SoEvent * const event)
2137{
2138  if (PRIVATE(this)->overlayManager->processEvent(event)) { return TRUE; }
2139  if (PRIVATE(this)->normalManager->processEvent(event)) { return TRUE; }
2140  return FALSE;
2141}
2142
2143// *************************************************************************
2144
2145/*!
2146  Overrides So@Gui@GLWidget::processEvent() to attempt to convert
2147  toolkit-native events to Coin-generic events.  If this succeeds, the
2148  generic SoEvent is forwarded to So@Gui@RenderArea::processSoEvent().
2149 */
2150void
2151So@Gui@RenderArea::processEvent(@EVENT@ event)
2152{
2153  // FIXME: This method is not reentrant, but certain GUI toolkits may
2154  // run the event-loop recursively with events injected while processing
2155  // other events. We must detect when this happens, and queue up the
2156  // event for delayed processing after the primary event is done processing.
2157  // This means event instances can not be static and rewritten in the
2158  // device handlers like they are currently...  20051013 larsa
2159
2160  if (PRIVATE(this)->invokeAppCB(event)) { return; }
2161
2162  const SoEvent * soevent = PRIVATE(this)->getSoEvent(event);
2163
2164  if (soevent != NULL) {
2165#if SO@GUI@_DEBUG || defined(DEBUGGING_EGGS)
2166    // Undocumented feature: there are several "magic" sequences of
2167    // keys when tapped into the rendering canvas which'll pop up a
2168    // dialog box with information about that particular feature.
2169    //
2170    // See code comments behind "case" statements below for which
2171    // sequences are available so far.
2172
2173    if (soevent->isOfType(SoKeyboardEvent::getClassTypeId())) {
2174      SoKeyboardEvent * ke = (SoKeyboardEvent *)soevent;
2175      if (ke->getState() == SoButtonEvent::UP) {
2176        char c = ke->getPrintableCharacter();
2177        switch (PRIVATE(this)->checkMagicSequences(c)) {
2178        case So@Gui@RenderAreaP::NONE:
2179          break;
2180        case So@Gui@RenderAreaP::OPENGL:  // "glinfo"
2181          this->glLockNormal();
2182          PRIVATE(this)->showOpenGLDriverInformation();
2183          this->glUnlockNormal();
2184          break;
2185        case So@Gui@RenderAreaP::INVENTOR:  // "ivinfo"
2186          PRIVATE(this)->showInventorInformation();
2187          break;
2188        case So@Gui@RenderAreaP::TOOLKIT:  // "soinfo"
2189          PRIVATE(this)->showToolkitInformation();
2190          break;
2191        case So@Gui@RenderAreaP::DUMPSCENEGRAPH:  // "dumpiv"
2192          PRIVATE(this)->dumpScenegraph();
2193          break;
2194        case So@Gui@RenderAreaP::DUMPCAMERAS:  // "cameras"
2195          PRIVATE(this)->dumpCameras();
2196          break;
2197        case So@Gui@RenderAreaP::OFFSCREENGRAB:  // "osgrab"
2198          PRIVATE(this)->offScreenGrab();
2199          break;
2200        default:
2201          assert(FALSE && "unknown debug key sequence");
2202          break;
2203        }
2204      }
2205    }
2206#endif // SO@GUI@_DEBUG
2207
2208    SbBool processed = this->processSoEvent(soevent);
2209    if (processed) return;
2210  }
2211
2212  inherited::processEvent(event);
2213}
2214
2215// *************************************************************************
2216
2217// doc from parent
2218SbBool
2219So@Gui@RenderArea::glScheduleRedraw(void)
2220{
2221  this->scheduleRedraw();
2222  if (this->hasOverlayGLArea() && this->getOverlaySceneGraph()) {
2223    this->scheduleOverlayRedraw();
2224  }
2225  return TRUE;
2226}
2227
2228// *************************************************************************
2229
2230/*!
2231  This method posts and processes an SoEvent object to the
2232  So@Gui@RenderArea-based component and returns the result value from the
2233  event handler.  This is a synchronous operation.
2234*/
2235SbBool
2236So@Gui@RenderArea::sendSoEvent(const SoEvent * event)
2237{
2238  return this->processSoEvent(event);
2239}
2240
2241// *************************************************************************
2242
2243#undef PRIVATE
2244#undef PUBLIC
2245
2246