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