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/* For the Doxygen tool. */
36
37/*! \defgroup misc Miscellaneous Classes */
38/*! \defgroup devices @Gui@ Device Classes */
39/*! \defgroup components @Gui@ Components */
40/*! \defgroup viewers @Gui@ Viewer Components */
41
42
43// FIXME: the code example shouldn't be duplicated here -- it's
44// already part of the SoQt mainpage-doc. 20020806 mortene.
45
46/*!
47  \class So@Gui@ So@Gui@.h Inventor/@Gui@/So@Gui@.h
48  \brief The So@Gui@ class takes care of @Gui@ initialization and event dispatching.
49
50  \ingroup misc
51
52  This is the "application-wide" class with solely static methods
53  handling initialization and event processing tasks. You must use
54  this class in any application built on top of the So@Gui@
55  library.
56
57  Typical usage is as follows (complete application code):
58
59  \code
60  #include <Inventor/@Gui@/So@Gui@.h>
61  #include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h>
62  #include <Inventor/nodes/SoBaseColor.h>
63  #include <Inventor/nodes/SoCone.h>
64  #include <Inventor/nodes/SoSeparator.h>
65
66  int
67  main(int argc, char ** argv)
68  {
69    // Initialize So@Gui@ and Inventor API libraries. This returns a main
70    // window to use.
71    @WIDGET@ mainwin = So@Gui@::init(argc, argv, argv[0]);
72
73    // Make a dead simple scene graph, only containing a single
74    // yellow cone under the scenegraph root.
75    SoSeparator * root = new SoSeparator;
76    root->ref();
77
78    SoBaseColor * col = new SoBaseColor;
79    col->rgb = SbColor(1, 1, 0);
80    root->addChild(col);
81
82    root->addChild(new SoCone);
83
84    // Use one of the convenient viewer classes.
85    So@Gui@ExaminerViewer * eviewer = new So@Gui@ExaminerViewer(mainwin);
86    eviewer->setSceneGraph(root);
87    eviewer->show();
88
89    // Pop up the main window.
90    So@Gui@::show(mainwin);
91    // Loop until exit.
92    So@Gui@::mainLoop();
93
94    // Clean up resources.
95    delete eviewer;
96    root->unref();
97    So@Gui@::done();
98
99    return 0;
100  }
101  \endcode
102
103  And voila:
104
105  <center>
106  <img src="sogui-class-example.png">
107  </center>
108
109  \sa So@Gui@Component
110*/
111
112// *************************************************************************
113
114#include <stdlib.h>
115#include <string.h>
116
117#include <Inventor/@Gui@/So@Gui@.h>
118#include <Inventor/@Gui@/SoGuiP.h>
119#include <Inventor/@Gui@/SoAny.h>
120
121#include <Inventor/SoDB.h>
122#include <Inventor/SoInteraction.h>
123#include <Inventor/nodekits/SoNodeKit.h>
124
125#include <string>
126
127// *************************************************************************
128
129// Default constructor and the destructor is private (So@Gui@ class
130// consists solely of static functions).
131So@Gui@::So@Gui@(void) { }
132So@Gui@::~So@Gui@() { }
133
134// *************************************************************************
135
136cc_mutex * SoGuiP::gllockmutex = NULL;
137
138// *************************************************************************
139
140void
141SoGuiP::commonInit(void)
142{
143  SoDB::init();
144  SoNodeKit::init();
145  SoInteraction::init();
146
147  SoAny::init();
148
149  SoGuiP::gllockmutex = cc_mutex_construct();
150}
151
152void
153SoGuiP::commonCleanup(void)
154{
155  cc_mutex_destruct(SoGuiP::gllockmutex);
156}
157
158// *************************************************************************
159
160/*!
161  This function initializes the So@Gui@ library, as well as the
162  Inventor API. The latter is done by calling \a SoDB::init(), \a
163  SoNodeKit::init() and \a SoInteraction::init().
164
165  The returned @WIDGET@ is a toplevel shell widget for the
166  application, which can be used as a shell for the main component.
167
168  This method is provided for easier porting / compatibility with the
169  original SGI Inventor InventorXt library. It just adds dummy \a argc
170  and \a argv arguments and calls the So@Gui@::init() method below.
171*/
172@WIDGET@
173So@Gui@::init(const char * appname, const char * classname)
174{
175  // If this is SoQt, Qt will keep a reference to the appname string
176  // -- so make it guaranteed permanent.
177  char * buf = NULL;
178  if (appname != NULL) {
179    size_t appname_len = strlen(appname);
180    buf = (char *)new char[appname_len+1]; // FIXME: minor memleak, free on app-exit. 20020117 mortene.
181    (void)strncpy(buf, appname, appname_len);
182    buf[appname_len] = '\0';
183  }
184
185  // FIXME: Both SoGui_init_argc/argv need to be static to workaround
186  // a compiler optimization memory corruption bug (observed with gcc
187  // 4.1.1 using -O2 in combination with qt-4.2.x), which would lead
188  // to a segfault. 20070528 tamer.
189
190  // Fake argc and argv setup, forward to real init routine.
191  static int SoGui_init_argc = (buf != NULL) ? 1 : 0;
192  static char * SoGui_init_argv[2]; // use static array to avoid memory corruption in Qt
193  SoGui_init_argv[0] = buf;
194  SoGui_init_argv[1] = NULL;
195
196  return So@Gui@::init(SoGui_init_argc, SoGui_init_argv, appname, classname);
197}
198
199/*!
200  \fn @WIDGET@ So@Gui@::init(int & argc, char ** argv, const char * appname, const char * classname)
201
202  This function initializes the So@Gui@ library, as well as the
203  Inventor API. The latter is done by calling \a SoDB::init(), \a
204  SoNodeKit::init() and \a SoInteraction::init().
205
206  Creates an application framework and constructs and returns a main
207  widget for you.
208
209  The returned @WIDGET@ is a toplevel shell widget for the
210  application, which can be used as a shell for the main component.
211*/
212
213/*!
214  \fn void So@Gui@::init(@WIDGET@ toplevelwidget)
215
216  This function initializes the So@Gui@ library, as well as the
217  Inventor API. The latter is done by calling \a SoDB::init(), \a
218  SoNodeKit::init() and \a SoInteraction::init().
219
220  Assumes you are creating your own application framework and main
221  widget.  \a toplevelwidget should be your application's main widget.
222*/
223
224/*!
225  \fn void So@Gui@::done(void)
226
227  Cleans up all static data allocated by the So@Gui@ library on
228  initialization.
229
230  This functions calls SoDB::finish() which means that no Coin classes
231  should be used after it has been called.
232
233  <!-- (Disabled: see FIXME in SoQt.cpp:855)
234  Is called implicitly from the end of So@Gui@::mainLoop(), so the
235  application programmer should not call it explicitly unless she
236  has taken control over the native event loop herself, and is
237  therefore not using So@Gui@::mainLoop().
238
239  (Even then it is usually not necessary to call this method, as the
240  operating system should take care of cleaning up resource use when
241  an application exits. This method is mainly provided as a manner to
242  be able to clean up in "unusual" run-time environments, for instance
243  if So@Gui@ is used as a component in a browser plug-in.)
244  -->
245
246  It should never be invoked more than \e once, and that
247  is just before application exit, as it deallocates \e static data
248  allocated as "one-off" operations in So@Gui@::init().
249
250  NOTE: done() is not present in the original API for SGI's InventorXt
251  library.
252*/
253
254/*!
255  \fn void So@Gui@::mainLoop(void)
256
257  This is the event dispatch loop.
258
259  It doesn't return until application exit is somehow forced, either
260  programmatically from the relevant API-call of the native toolkit,
261  or when the user closes the application's main widget.
262
263  After the main loop has finished execution, call So@Gui@::done() to clean
264  up static data.
265
266  <!-- (Disabled: see FIXME in SoQt.cpp:855)
267  An important note: be careful about how you handle
268  So@Gui@Component-derived objects after the application control returns
269  from mainLoop(), as So@Gui@ will then have been "cleaned up" with
270  regards to various internal resources. So doing for instance
271  something like this:
272
273  \code
274  So@Gui@::mainLoop();
275  viewer->hide();
276  \endcode
277
278  ..spells "undefined behavior, expect a crash".
279
280  \e Deleting a component after mainLoop() returns is allowed, though,
281  and also necessary to avoid getting reports of possible memory leaks
282  from memleak checkers.
283  -->
284*/
285
286/*!
287  \fn void So@Gui@::exitMainLoop(void)
288
289  This function will make the main event loop finish looping.
290
291  NOTE: exitMainLoop() is not present in the original API for SGI's
292  InventorXt library.
293*/
294
295/*!
296  \fn @WIDGET@ So@Gui@::getTopLevelWidget(void)
297
298  Returns the @WIDGET@ which is the main widget for the
299  application. When this widget gets closed, So@Gui@::mainLoop() will
300  return (unless the close event is caught by the user).
301
302  \sa getShellWidget()
303*/
304
305// *************************************************************************
306
307/*!
308  This method will fill in the integers pointed to by the arguments
309  with the corresponding part of the version release number of the
310  So@Gui@ library.
311
312  A \c NULL pointer will make that part of the version number be ignored.
313
314  This method is not part of the original InventorXt API from SGI.
315*/
316void
317So@Gui@::getVersionInfo(int * major, int * minor, int * micro)
318{
319  if (major) { *major = SO@GUI@_MAJOR_VERSION; }
320  if (minor) { *minor = SO@GUI@_MINOR_VERSION; }
321  if (micro) { *micro = SO@GUI@_MICRO_VERSION; }
322}
323
324/*!
325  This method returns a string containing the version id of the
326  library.
327
328  This method is not part of the original InventorXt API from SGI.
329*/
330const char *
331So@Gui@::getVersionString(void)
332{
333  static const char version[] = SO@GUI@_VERSION;
334  return version;
335}
336
337/*!
338  This method returns a string containing the version id of the
339  @Gui@ toolkit the So@Gui@ library is linked against.
340
341  This method is not part of the original InventorXt API from SGI.
342*/
343#ifdef SOWIN_INTERNAL
344namespace Win32 {
345  std::string GetOSDisplayString();
346};
347#endif
348
349const char *
350So@Gui@::getVersionToolkitString(void)
351{
352#ifdef SOWIN_INTERNAL
353  static std::string toolkit_version = Win32::GetOSDisplayString();
354#else
355  static std::string toolkit_version = GUI_TOOLKIT_VERSION;
356#endif
357  return toolkit_version.c_str();
358}
359
360// *************************************************************************
361
362/*!
363  This method locks other threads out from a code section until the
364  caller thread invokes So@Gui@::unlockGL().
365
366  It is meant to protect several threads invoking OpenGL calls in
367  parallell, in case the underlying OpenGL implementation is not
368  multi-thread safe.
369
370  For convenience, the function can be invoked through the
371  SO@GUI@_ENTER_GL_SECTION().
372
373  This method is not part of the original InventorXt API from SGI.
374*/
375void
376So@Gui@::lockGL(void)
377{
378  assert(SoGuiP::gllockmutex);
379
380  cc_mutex_lock(SoGuiP::gllockmutex);
381}
382
383/*!
384  See documentation for So@Gui@::lockGL().
385
386  For convenience, the function can be invoked through the
387  SO@GUI@_LEAVE_GL_SECTION().
388
389  This method is not part of the original InventorXt API from SGI.
390*/
391void
392So@Gui@::unlockGL(void)
393{
394  cc_mutex_unlock(SoGuiP::gllockmutex);
395}
396
397// *************************************************************************
398
399/*!
400  \typedef void So@Gui@::FatalErrorCB(const SbString errmsg, So@Gui@::FatalErrors errcode, void * userdata)
401
402  An application function callback for handling fatal errors should be
403  of this type.
404
405  The first argument is an error message in English describing the
406  details of the error. The second argument is an error code used so
407  the application can identify specific conditions. The third argument
408  is the userdata pointer passed in to So@Gui@::setFatalErrorHandler().
409*/
410
411/*!
412  \enum So@Gui@::FatalErrors
413  Numerical identifiers for classifying the different kinds of possible
414  fatal errors.
415*/
416
417/*!
418  \var So@Gui@::FatalErrors So@Gui@::NO_OPENGL_CANVAS
419
420  Could not construct \e any valid OpenGL canvas. Something is very
421  wrong on the client system.
422*/
423/*!
424  \var So@Gui@::FatalErrors So@Gui@::INTERNAL_ASSERT
425
426  An internal error condition that should never happen was
427  detected. The most likely cause of this is programmering errors
428  within the So@Gui@ library itself.
429*/
430/*!
431  \var So@Gui@::FatalErrors So@Gui@::UNSPECIFIED_ERROR
432
433  Signifies that we were not able to specify in any greater detail the
434  error condition that came up.
435*/
436
437/*!
438  Set up a callback to invoke in the case of unexpected fatal error
439  conditions within the So@Gui@ library.
440
441  Almost any error condition within the library is handled in a robust
442  way through return values indicating errors for the offending calls,
443  but there are a few cases that are impossible to handle without
444  seriously crippling the functionality.
445
446  (One example is if we fail to find \e any way of making a valid
447  OpenGL canvas. This is an indication that something is seriously
448  wrong on the end-user's system, and the So@Gui@ library will not work
449  properly.)
450
451  In the case of a fatal error, it is expected that the given
452  application callback function communicates the problem to the
453  end-user and then either exits the application or at least refrains
454  from using any part of the So@Gui@ library.
455
456  If no callback is explicitly set up by the application, the So@Gui@
457  library will display an error message to the end-user and then exit
458  the application.
459
460  When setting a callback, this method returns a pointer to the
461  previous callback function, or \c NULL if none.
462
463  (This is an extension versus the original SGI InventorXt library
464  API.)
465
466
467  On a related note, be aware that the end-user will still be notified
468  about non-fatal errors and warning messages through a dialog box. If
469  you want to handle these yourself, or if you don't want your
470  end-user to see any of these non-critical messages, you will also
471  need to install an error handler callback function on the Coin
472  library itself:
473
474  \code
475    SoDebugError::setHandlerCallback(myErrorHandlerCB, myCBUserData);
476  \endcode
477
478  (Please also see the documentation of
479  SoDebugError::setHandlerCallback().)
480
481 */
482So@Gui@::FatalErrorCB *
483So@Gui@::setFatalErrorHandler(So@Gui@::FatalErrorCB * cb, void * userdata)
484{
485  return SoAny::si()->setFatalErrorHandler(cb, userdata);
486}
487
488// *************************************************************************
489
490/*!
491  Returns \c TRUE if this binary version of the So@Gui@ library was
492  compiled with debug settings on.
493
494  This method was not part of the original SGI InventorXt library, but
495  is an extension specific to the Coin project.
496*/
497SbBool
498So@Gui@::isDebugLibrary(void)
499{
500  return SO@GUI@_DEBUG != 0 ? TRUE : FALSE;
501}
502
503/*!
504  Returns \c TRUE if this version of the So@Gui@ library has an
505  Application Binary Interface compatible with the given version.
506
507  This method was not part of the original SGI InventorXt library, but
508  is an extension specific to the Coin project.
509*/
510SbBool
511So@Gui@::isCompatible(unsigned int major, unsigned int minor)
512{
513  if (major != SO@GUI@_MAJOR_VERSION) { return FALSE; }
514  if (minor > SO@GUI@_MINOR_VERSION) { return FALSE; }
515  return TRUE;
516}
517
518/*!
519  \enum So@Gui@::ABIType
520  Numerical identifiers to identify how the library was built.
521*/
522/*!
523  \var So@Gui@::ABIType So@Gui@::DLL
524  The So@Gui@ library was built as a dynamic link library (aka "shared
525  library").
526*/
527/*!
528  \var So@Gui@::ABIType So@Gui@::LIB
529  The So@Gui@ library was built as a static library.
530*/
531/*!
532  \var So@Gui@::ABIType So@Gui@::UNKNOWN
533  The So@Gui@ introspection mechanisms can not decide how the library
534  was built.
535*/
536
537
538/*!
539  Returns an indication on how the library was compiled: as a dynamic
540  library, or as a static library.
541
542  This method was not part of the original SGI InventorXt library, but
543  is an extension specific to the Coin project.
544*/
545So@Gui@::ABIType
546So@Gui@::getABIType(void)
547{
548  // FIXME: replace this ugly shit with a configure-check to test if
549  // we're on a DLL-capable platform. 20020118 mortene.
550#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
551#ifdef SO@GUI@_MAKE_DLL
552  return So@Gui@::DLL;
553#else
554  return So@Gui@::LIB;
555#endif
556#endif
557  // FIXME: this is rather lame, we have enough information in the
558  // configure / build process to always know whether we're building
559  // static or dynamic. 20020618 mortene.
560  return So@Gui@::UNKNOWN;
561}
562
563#if 0 // FIXME: not in use, see larsa's FIXME below. 20020617 mortene.
564
565// *************************************************************************
566// These sanity checks are designed to detect common pitfall errors for
567// Microsoft Windows linkage with So@Gui@ and Coin.
568
569// FIXME: use an "is-this-a-DLL-capable-platform" configure check
570// and remove the system "#if defined" tests below. 20011203 mortene.
571// FIXME: I disabled this because it wasn't inlined in the client app
572// but compiled into the library by MSVC++ and with SOWIN_DLL undefined,
573// the ABI test always tried the LIB_ABI path.  20020126 larsa
574#if 0 && (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
575#define SO@GUI@_SANITY_CHECK(forward_call)                  \
576  if (So@Gui@::isDebugLibrary() &&                       \
577       SoDB::isInitialized())                          \
578    SoDebugError::postWarning("So@Gui@::init",            \
579      "unable to verify application linkage sanity "    \
580      "because Open Inventor has already been "         \
581      "initialized before So@Gui@::init call");           \
582  SO@GUI@_RUNTIME_SANITY_CHECK();                         \
583  SO@GUI@_LINKSTYLE_SANITY_CHECK();                       \
584  forward_call;                                         \
585  SO@GUI@_LINKTIME_SANITY_CHECK()
586
587#define SO@GUI@_RUNTIME_SANITY_CHECK()                    \
588  do {                                                  \
589    if (!So@Gui@::isCompatible(SO@GUI@_MAJOR_VERSION,      \
590                            SO@GUI@_MINOR_VERSION))      \
591      So@Gui@::abort(SO@GUI@_RUNTIME_MISMATCH);             \
592  } while (FALSE)
593
594#ifdef SO@GUI@_DLL
595#define SO@GUI@_LINKSTYLE_SANITY_CHECK()                  \
596  do {                                                  \
597    if (So@Gui@::getABIType() != SO@GUI@_DLL_ABI)         \
598      So@Gui@::abort(SO@GUI@_LINKSTYLE_MISMATCH);           \
599  } while (FALSE)
600#else
601#define SO@GUI@_LINKSTYLE_SANITY_CHECK()                  \
602  do {                                                  \
603    if (So@Gui@::getABIType() != SO@GUI@_LIB_ABI)         \
604      So@Gui@::abort(SO@GUI@_LINKSTYLE_MISMATCH);           \
605  } while (FALSE)
606#endif
607
608#define SO@GUI@_LINKTIME_SANITY_CHECK()                   \
609  do {                                                  \
610    if (!SoDB::isInitialized())                       \
611      So@Gui@::abort(SO@GUI@_LINKTIME_MISMATCH);            \
612  } while (FALSE)
613#else /* ! MS Windows */
614#define SO@GUI@_SANITY_CHECK(forward_call)                \
615  forward_call
616#endif /* ! MS Windows */
617
618#endif // OBSOLETED
619
620#ifndef DOXYGEN_SKIP_THIS
621
622// Abort the application due to some kind of mismatch in the ABI
623// settings / configuration. This should hopefully help application
624// programmers avoid shooting themselves in the foot by controlling
625// certain run-time parameters in the client application versus what
626// is expected by the So@Gui@ library.
627//
628// If you're an application programmer, it is very likely that you
629// only need to consider this an internal library method.
630//
631// This method was not part of the original SGI InventorXt library,
632// but is an extension specific to the Coin project.
633void
634SoGuiP::abort(SoGuiP::ABIError error)
635{
636  switch (error) {
637  case SoGuiP::LINKTIME_MISMATCH:
638    So@Gui@::createSimpleErrorDialog(NULL, "Fatal Error",
639                                     "Detected linktime mismatch error.");
640    break;
641
642  case SoGuiP::LINKSTYLE_MISMATCH:
643    So@Gui@::createSimpleErrorDialog(NULL, "Fatal Error",
644                                     "Detected linkstyle mismatch error (DLL vs. LIB).");
645    break;
646
647  case SoGuiP::RUNTIME_MISMATCH:
648    So@Gui@::createSimpleErrorDialog(NULL, "Fatal Error",
649                                     "Detected runtime mismatch error (versioning and ABI compatibility).");
650    break;
651
652  default:
653    // FIXME: shouldn't this rather be an assert? 20020118 mortene.
654    So@Gui@::createSimpleErrorDialog(NULL, "Fatal Error",
655                                     "Unknown error in So@Gui@ :(");
656    break;
657  }
658
659  // FIXME: call fatal error handler in SoAny. 20020118 mortene.
660  exit(-1);
661}
662
663#endif // DOXYGEN_SKIP_THIS
664
665// *************************************************************************
666
667/*!
668  \fn void So@Gui@::createSimpleErrorDialog(@WIDGET@ widget, const char * title, const char * string1, const char * string2)
669
670
671  This is a convenient way for the application programmer to throw up
672  an obtrusive application-global error dialog.
673
674  If \a widget is \c NULL, the dialog will be modal for the whole
675  application (all windows will be blocked for input). If not,
676  only the window for the given \a widget will be blocked.
677
678  \a title is the title of the dialog box. \a string1 and \a string2
679  contains the text which will be shown in the dialog box.
680
681  There will only be a single "Ok" button for the user to press and
682  continue with the application.
683*/
684