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