VR Juggler Startup

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.

No main()—Sort Of

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.

Structure of a main() Function

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 {                                                                         (1)
       vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel       (2)
       simpleApp* app      = new simpleApp();         // Create the app object
                                                                              (3)
       kernel->loadConfigFile(...);             // Configure the kernel       (4)
 10    kernel->start();                         // Start the kernel thread    (5)
       kernel->setApplication(app);             // Give application to kernel
       kernel->waitForKernelStop();             // Block until kernel stops
    
       return 0;
 15 }

1

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.

2

We instantiate a copy of the user application object (simpleApp) here. Notice that we include the header file that defines the simpleApp class.

3

This statement represents the code that will be in the main() function for passing configuration files to the kernel's loadConfigFile() method. These configuration files may come from the command line or from some other source. If reading the files from the command line, it can be as simple as looping through all the arguments and passing each one to the kernel.

4

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.

5

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.

Mac OS X Considerations

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.

Application Bundles

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.

Info.plist

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.

PkgInfo

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.

Resources Directory

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.

vrjuggler.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 Open With 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 File menu defined in the default NIB (see below).

en.lproj Directory

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.

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 File 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.

Application Execution

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.

Relying Exclusively on Command Line Arguments to Launch

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.

Handling No Command Line Arguments at Launch Time

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;
    }

Allowing Optional Use of Command Line Arguments

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 File 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.