In this section, we describe one way to start VR Juggler. We
will use the traditional main() function in
C++, but this is not the only way to do it. We have written Python
applications that start the VR Juggler kernel, and it is possible
to write a VR Juggler daemon that loads applications
on demand at runtime. In other words, the VR Juggler startup
procedure is quite flexible, and we choose to focus on the
simplest method here.
Previously, we explained how VR Juggler applications do
not have a main() function, but further
explanation is required. While it is true that user
applications do not have a
main() function because they are objects,
there must still be a main() somewhere
that starts the system. This is because the operating system
uses main() as the starting point for all
applications. In typical VR Juggler applications, there is a
main(), but it only starts the VR Juggler
kernel and gives the kernel the application to run. It then
waits for the kernel to shut down before exiting.
The following is a typical example of a
main() function that will start the VR
Juggler kernel and hand it an instance of a user application
object. The specifics of what is happening in this code are
described below.
1 #include <vrj/Kernel/Kernel.h>
#include <simpleApp.h>
int main(int argc, char* argv[])
5 {
vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel
simpleApp* app = new simpleApp(); // Create the app object
kernel->loadConfigFile(...); // Configure the kernel
10 kernel->start(); // Start the kernel thread
kernel->setApplication(app); // Give application to kernel
kernel->waitForKernelStop(); // Block until kernel stops
return 0;
15 }
| This line finds (and may create) the VR Juggler kernel. The kernel reference is stored in the handle so that we can use it later. |
| We instantiate a copy of the user application
object ( |
| This statement represents the code that will be
in the |
| As a result of this statement, the VR Juggler kernel begins running. It creates a new thread of execution for the kernel, and the kernel begins its internal processing. From this point on, any changes made reconfigure the kernel. These changes can come in the form of more configuration files or in the form of an application object to execute. At this point, it is important to notice that the kernel knows nothing about the application. Moreover, there is no need for it to know about configuration files yet. This demonstrates how the VR Juggler kernel executes independently from the user application. The kernel will simply work on its own controlling and configuring the system even without an application to run. |
| This statement finally tells the kernel what application it should run. The method call reconfigures the kernel so that it will now start invoking the application object's member functions. It is at this time that the application is now running in the VR system. |
VR Juggler 2.2 introduces support for Cocoa on Mac OS X.
Cocoa itself is quite different than the X11 or Win32 APIs as
far as restrictions on threading and the implementation of the
main() function. The fundamental issue,
however, is that both the NSApplication
singleton object and the vrj::Kernel
singleton object want to be in charge of application execution.
A balance has been struck that generally allows VR Juggler
applications to look and act the same on Mac OS X as on any
other platform while still taking advantage of unique features
that Cocoa and Mac OS X have to offer.
First and foremost, when using a Cocoa-aware version of VR Juggler, applications have to be constructed as bundles. The details of OS X application bundles are far beyond the scope of this document. However, VR Juggler provides just about everything that is required to make an application bundle. This makes sense because VR Juggler is an application framework that dictates how applications are written and executed. Thus, it also provides the core information required for proper application construction and execution on Mac OS X.
To get started, it is helpful to understand what the
application bundle will look like when we have everything in
place. An application bundle is a directory structure with a
special name. For example, MPApp.app
will show up in the Finder as an
application named “MPApp” that can be
double-clicked. The name can contain spaces if so
desired.
Under the base directory is the
Contents subdirectory. It will contain
the files Info.plist and
PkgInfo.
Next, there are two subdirectories
MacOS and
Resources. Compiled binaries should
normally go into the MacOS
subdirectory. If nothing else, the bundle executable (the
compiled application binary) must go in
the MacOS directory. Shared libraries
can go into the Resources directory if
desired. Generally, though, the
Resources directory will contain
platform-independent data files.
In the resources directory, there will be another
subdirectory en.lproj[1] which in turn contains
MainMenu.nib from
$VJ_BASE_DIR/share/vrjuggler/data/bundle.
This is a critical part of the application bundle, and it is
very important that this NIB behave correctly. The NIB
defines the user interface for the VR Juggler application,
and what is provided with VR Juggler is set up and ready to
go for the most common cases. It is possible to use a
different MainMenu.nib, but customizing
it will require care.
The files and directories needed for application
bundle creation can be found in the directory
$VJ_BASE_DIR/share/vrjuggler/data/bundle.
The first of these is
Contents/Info.plist, the basic
property list for an application bundle. Open it with the
Property List Editor application and change the
@APP_NAME@ strings accordingly for the
name of the application being constructed. For the
CFBundleExecutable property, be sure
to change @APP_NAME@ to be the name of
the executable that will be in the Contents/MacOS
directory. Other properties to change are those that
include copyright and version information.
The contents of
Contents/PkgInfo define the
application bundle as an application bundle. The contents
of the file will often be APPL????,
though other characters are allowed in place of the
???? part.
The Contents/Resources
directory contains data files related to application
execution. The file vrjuggler.plist,
the use of which is highly recommended, must be put in
this directory. The application bundle icon file (a file
with the extension .icns) is also
stored in this directory. A default icon file,
vrjuggler.icns, can be used, or a
custom one can be made. The file to use must be named in
Contents/Info.plist.
For VR Juggler application bundles, a useful file
is vrjuggler.plist. The default
version of this file from
$VJ_BASE_DIR/share/vrjuggler/data/bundle
disables VR Juggler configuration file loading through
NSApplication channels and
identifies the NSApplication
delegate class type that will be used (see the section called “Making a Custom NSApplication
Delegate on Mac OS X”). These are set
using the properties VRJConfigHandling
and VRJDelegateClass
respectively.
The “configuration file loading through
NSApplication channels” bit
has to do with associating VR Juggler configuration files
with application bundles. There are different means by
which data can be delivered to applications on Mac OS X.
For example, double-clicking on a file in the
Finder (or selecting
in the context menu
for the file) causes a registered handler application to
be opened. The file is given to the application once it
has opened through the
NSApplication event system. By
setting the VRJConfigHandling property
to false in
Contents/Resources/vrjuggler.plist,
this capability is disabled for the application bundle in
question. That is, the application bundle will ignore the
NSApplication events pertaining to
configuration files to be loaded as a result of
double-clicking on the said files. Configuration files
can still be opened on the fly using the
menu defined in the default NIB
(see below).
The en.lproj subdirectory of
Contents/Resources contains
information translated in the English language. The most
important item in this directory is MainMenu.nib
(discussed next), but the optional file
$VJ_BASE_DIR/share/vrjuggler/data/bundle/InfoPlist.strings,
or some other version of same, can be copied into this
directory. Other language-specific directories can be
created as subdirectories of
Contents/Resources as necessary.
They, too, must contain a
MainMenu.nib.
This is the NIB for the VR juggler application
bundle. It has been designed using Interface Builder with
a simple user interface that knows how to interact with a
VR Juggler application. The interface includes the
menu with the item for opening VR
Juggler configuration files on the fly. Other menus and
menu items can be added by making a custom MainMenu.nib.
Using the version from
$VJ_BASE_DIR/share/vrjuggler/data/bundle
as a starting point is a good idea. Note that
MainMenu.nib is a directory, and it
should be copied recursively to
Contents/Resources/en.lproj.
The main() function
implementation show in the section called “Structure of a main()
Function” will work without
any problems on Mac OS X. However, many of the VR Juggler
sample and test applications have a slightly more
complicated main() function. In
particular, these applications usually determine whether the
user has passed in any arguments through the command line
and exit with an error message explaining how to run the
application if none were given. The example below shows how
this is commonly done with an if
statement before anything else:
1 @include <iostream>
#include <cstdlib>
#include <vrj/Kernel/Kernel.h>
#include <simpleApp.h>
5
int main(int argc, char* argv[])
{
if ( argc <= 1 )
{
10 std::cout << "Usage: " << argv[0]
<< "cfgfile[0] cfgfile[1] ... cfgfile[n]"
<< std::endl;
std::exit(EXIT_FAILURE);
}
15
vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel
simpleApp* app = new simpleApp(); // Create the app object
kernel->loadConfigFile(...); // Configure the kernel
20 kernel->start(); // Start the kernel thread
kernel->setApplication(app); // Give application to kernel
kernel->waitForKernelStop(); // Block until kernel stops
return 0;
25 }This approach will not necessarily work on Mac OS X
because users normally expect to be able to launch an
application by double-clicking on its icon in the
Finder and then loading data into
it through a GUI. If a user launched the above application
that way, the application would exit immediately, and the
user would have to open the
Console application to find out
what went wrong. Launching from the command line, while
perfectly valid on Mac OS X, is simply not what users
expect. At the same time, remote launching of VR juggler
applications in a cluster will almost certainly require
launching without the use of the
Finder and passing in arguments
through argv. This is definitely the case
when using Maestro.
If a VR Juggler application will be run on Mac OS X, the programmer has to decide whether command line processing is critical to the execution of the application. There are three options available to VR Juggler programmers on Mac OS X: rely exclusively on command line arguments to launch, handle no command line arguments, or allow the use of command line arguments but do not require them. Given that VR Juggler applications written on all other platforms are highly likely to rely exclusively on command line arguments to launch, the first choice is expected to be the most commonly chosen approach. Nevertheless, we will explain all three.
If a VR Juggler application used on Mac OS X will
rely solely on command line arguments to launch, it is
operating in exactly the same manner as on all other
platforms. The main() function can
be written as is shown in this document to exit if no
command line arguments are supplied. There is still one
more thing to do, though. In the
Resources directory of the
application bundle, there must be a property list file in
the bundle called
Contents/Resources/vrjuggler.plist.
This property list must have the property
VRJConfigHandling set to
false. This is the default setting for
this property if the vrjuggler.plist
file that comes with VR Juggler was used in constructing
the application bundle.
To execute the application, run the program in
<appname>.app/Contents/MacOS
from a command prompt and pass in the command line
arguments. The appropriate event handling will be set up
so that the application will behave just as any other Mac
OS X application.
If no command line arguments are to be handled
(i.e., the application is to behave the same as typical
Mac OS X applications), then the
main() function shown above needs to
change. Specifically, it cannot exit with an error
message if no command line arguments are passed in. To
keep the application portable, a preprocessor conditional
can be used, as shown in Example 2.1, “Ignoring Command Line Arguments on Mac OS
X”. Then, in
Contents/Resources/vrjuggler.plist,
set the VRJConfigHandling property to
true. To execute the application,
double-click the application bundle icon in the
Finder or use the
open command from a command
prompt.
Example 2.1. Ignoring Command Line Arguments on Mac OS X
1 @include <iostream>
#include <cstdlib>
#include <vrj/Kernel/Kernel.h>
#include <simpleApp.h>
5
int main(int argc, char* argv[])
{
#if ! defined(VRJ_USE_COCOA)
if ( argc <= 1 )
10 {
std::cout << "Usage: " << argv[0]
<< "cfgfile[0] cfgfile[1] ... cfgfile[n]"
<< std::endl;
std::exit(EXIT_FAILURE);
15 }
#endif
vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel
simpleApp* app = new simpleApp(); // Create the app object
20
#if ! defined(VRJ_USE_COCOA)
kernel->loadConfigFile(...); // Configure the kernel
#endif
kernel->start(); // Start the kernel thread
25 kernel->setApplication(app); // Give application to kernel
kernel->waitForKernelStop(); // Block until kernel stops
return 0;
}A compromise can be struck between the previous two
options by allowing optional use of command line
arguments. The compromise is simple: do not require
command line arguments but still handle them if they are
given. The form of the main() function that allows this
is shown in Example 2.2, “Handling Optional Command Line Arguments on Mac
OS X”. Note that
the #if around the call to
vrj::Kernel::loadConfigFile()
has been removed.
Example 2.2. Handling Optional Command Line Arguments on Mac OS X
1 @include <iostream>
#include <cstdlib>
#include <vrj/Kernel/Kernel.h>
#include <simpleApp.h>
5
int main(int argc, char* argv[])
{
#if ! defined(VRJ_USE_COCOA)
if ( argc <= 1 )
10 {
std::cout << "Usage: " << argv[0]
<< "cfgfile[0] cfgfile[1] ... cfgfile[n]"
<< std::endl;
std::exit(EXIT_FAILURE);
15 }
#endif
vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel
simpleApp* app = new simpleApp(); // Create the app object
20
kernel->loadConfigFile(...); // Configure the kernel
kernel->start(); // Start the kernel thread
kernel->setApplication(app); // Give application to kernel
kernel->waitForKernelStop(); // Block until kernel stops
25
return 0;
}The only decision left to make is what value to use
for the VRJConfigHandling property in
Contents/Resources/vrjuggler.plist.
If we set it to false (recall that
that is the default in the
$VJ_BASE_DIR/share/vrjuggler/data/bundle/vrjuggler.plist
version), the application cannot be used as a handler for
VR Juggler configuration files. If we set it to true, we
have to be prepared for configuration files to come in
other than through the command line or through the use of
the menu. The default
NSApplication delegate
(VRJBasicDelegate) can deal with
either case. The default behavior for VR Juggler sample
applications is to allow configuration files to be
specified on the command line through the use of a
main() function similar to that
shown in Example 2.2, “Handling Optional Command Line Arguments on Mac
OS X” and to
leave the property VRJConfigHandling
in
Contents/Resources/vrjuggler.plist
set to false.
[1] This is the English language project data.
Translations of MainMenu.nib
would go into the appropriate language-specific
subdirectory.