| | 547 | </section> |
|---|
| | 548 | |
|---|
| | 549 | <section> |
|---|
| | 550 | <title>Mac OS X Considerations</title> |
|---|
| | 551 | |
|---|
| | 552 | <indexterm zone="section.app.main.structure"> |
|---|
| | 553 | <primary>applications</primary> |
|---|
| | 554 | |
|---|
| | 555 | <secondary>main function</secondary> |
|---|
| | 556 | |
|---|
| | 557 | <tertiary>Mac OS X</tertiary> |
|---|
| | 558 | </indexterm> |
|---|
| | 559 | |
|---|
| | 560 | <para>VR Juggler 2.2 introduces support for Cocoa on Mac OS X. |
|---|
| | 561 | Cocoa itself is quite different than the X11 or Win32 APIs as |
|---|
| | 562 | far as restrictions on threading and the implementation of the |
|---|
| | 563 | <function>main()</function> function. The fundamental issue, |
|---|
| | 564 | however, is that both the <classname>NSApplication</classname> |
|---|
| | 565 | singleton object and the <classname>vrj::Kernel</classname> |
|---|
| | 566 | singleton object want to be in charge of application execution. |
|---|
| | 567 | A balance has been struck that generally allows VR Juggler |
|---|
| | 568 | applications to look and act the same on Mac OS X as on any |
|---|
| | 569 | other platform while still taking advantage of unique features |
|---|
| | 570 | that Cocoa and Mac OS X have to offer.</para> |
|---|
| | 571 | |
|---|
| | 572 | <section> |
|---|
| | 573 | <title>Application Bundles</title> |
|---|
| | 574 | |
|---|
| | 575 | <para>First and foremost, when using a Cocoa-aware version |
|---|
| | 576 | of VR Juggler, applications have to be constructed as |
|---|
| | 577 | bundles. The details of OS X application bundles are far |
|---|
| | 578 | beyond the scope of this document. However, VR Juggler |
|---|
| | 579 | provides just about everything that is required to make an |
|---|
| | 580 | application bundle. This makes sense because VR Juggler is |
|---|
| | 581 | an application framework that dictates how applications are |
|---|
| | 582 | written and executed. Thus, it also provides the core |
|---|
| | 583 | information required for proper application construction and |
|---|
| | 584 | execution on Mac OS X.</para> |
|---|
| | 585 | |
|---|
| | 586 | <para>To get started, it is helpful to understand what the |
|---|
| | 587 | application bundle will look like when we have everything in |
|---|
| | 588 | place. An application bundle is a directory structure with a |
|---|
| | 589 | special name. For example, <filename>MPApp.app</filename> |
|---|
| | 590 | will show up in the <application>Finder</application> as an |
|---|
| | 591 | application named <quote>MPApp</quote> that can be |
|---|
| | 592 | double-clicked. The name can contain spaces if so |
|---|
| | 593 | desired.</para> |
|---|
| | 594 | |
|---|
| | 595 | <para>Under the base directory is the |
|---|
| | 596 | <filename>Contents</filename> subdirectory. It will contain |
|---|
| | 597 | the files <filename>Info.plist</filename> and |
|---|
| | 598 | <filename>PkgInfo</filename>.</para> |
|---|
| | 599 | |
|---|
| | 600 | <para>Next, there are two subdirectories |
|---|
| | 601 | <filename>MacOS</filename> and |
|---|
| | 602 | <filename>Resources</filename>. Compiled binaries should |
|---|
| | 603 | normally go into the <filename>MacOS</filename> |
|---|
| | 604 | subdirectory. If nothing else, the bundle executable (the |
|---|
| | 605 | compiled application binary) <emphasis>must</emphasis> go in |
|---|
| | 606 | the <filename>MacOS</filename> directory. Shared libraries |
|---|
| | 607 | can go into the <filename>Resources</filename> directory if |
|---|
| | 608 | desired. Generally, though, the |
|---|
| | 609 | <filename>Resources</filename> directory will contain |
|---|
| | 610 | platform-independent data files.</para> |
|---|
| | 611 | |
|---|
| | 612 | <para>In the resources directory, there will be another |
|---|
| | 613 | subdirectory <filename>en.lproj</filename><footnote> |
|---|
| | 614 | <para>This is the English language project data. |
|---|
| | 615 | Translations of <filename>MainMenu.nib</filename> |
|---|
| | 616 | would go into the appropriate language-specific |
|---|
| | 617 | subdirectory.</para> |
|---|
| | 618 | </footnote> which in turn contains |
|---|
| | 619 | <filename>MainMenu.nib</filename> from |
|---|
| | 620 | <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename>. |
|---|
| | 621 | This is a critical part of the application bundle, and it is |
|---|
| | 622 | very important that this NIB behave correctly. The NIB |
|---|
| | 623 | defines the user interface for the VR Juggler application, |
|---|
| | 624 | and what is provided with VR Juggler is set up and ready to |
|---|
| | 625 | go for the most common cases. It is possible to use a |
|---|
| | 626 | different <filename>MainMenu.nib</filename>, but customizing |
|---|
| | 627 | it will require care.</para> |
|---|
| | 628 | |
|---|
| | 629 | <sidebar> |
|---|
| | 630 | <para>The ability to customize the user interface of the |
|---|
| | 631 | VR Juggler application by using a different |
|---|
| | 632 | <filename>MainMenu.nib</filename> and a different |
|---|
| | 633 | application delegate (see <xref |
|---|
| | 634 | linkend="section.custom.delegate" />) makes VR Juggler on |
|---|
| | 635 | Mac OS X unique with respect to other platforms. Whereas |
|---|
| | 636 | the user interface used with <productname |
|---|
| | 637 | class="registered">Microsoft Windows</productname> and |
|---|
| | 638 | the X Window System is essentially hard coded, the Mac OS |
|---|
| | 639 | X Cocoa interface has been designed with flexibility in |
|---|
| | 640 | mind. Simply put, the Cocoa support in VR Juggler |
|---|
| | 641 | leverages the dynamic nature of Cocoa to provide features |
|---|
| | 642 | that are not so easily available on other |
|---|
| | 643 | platforms.</para> |
|---|
| | 644 | </sidebar> |
|---|
| | 645 | |
|---|
| | 646 | <section> |
|---|
| | 647 | <title><filename>Info.plist</filename></title> |
|---|
| | 648 | |
|---|
| | 649 | <para>The files and directories needed for application |
|---|
| | 650 | bundle creation can be found in the directory |
|---|
| | 651 | <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename>. |
|---|
| | 652 | The first of these is |
|---|
| | 653 | <filename>Contents/Info.plist</filename>, the basic |
|---|
| | 654 | property list for an application bundle. Open it with the |
|---|
| | 655 | Property List Editor application and change the |
|---|
| | 656 | <literal>@APP_NAME@</literal> strings accordingly for the |
|---|
| | 657 | name of the application being constructed. For the |
|---|
| | 658 | <literal>CFBundleExecutable</literal> property, be sure |
|---|
| | 659 | to change <literal>@APP_NAME@</literal> to be the name of |
|---|
| | 660 | the executable that will be in the Contents/MacOS |
|---|
| | 661 | directory. Other properties to change are those that |
|---|
| | 662 | include copyright and version information.</para> |
|---|
| | 663 | </section> |
|---|
| | 664 | |
|---|
| | 665 | <section> |
|---|
| | 666 | <title><filename>PkgInfo</filename></title> |
|---|
| | 667 | |
|---|
| | 668 | <para>The contents of |
|---|
| | 669 | <filename>Contents/PkgInfo</filename> define the |
|---|
| | 670 | application bundle as an application bundle. The contents |
|---|
| | 671 | of the file will often be <literal>APPL????</literal>, |
|---|
| | 672 | though other characters are allowed in place of the |
|---|
| | 673 | <literal>????</literal> part.</para> |
|---|
| | 674 | </section> |
|---|
| | 675 | |
|---|
| | 676 | <section> |
|---|
| | 677 | <title><filename>Resources</filename> Directory</title> |
|---|
| | 678 | |
|---|
| | 679 | <para>The <filename>Contents/Resources</filename> |
|---|
| | 680 | directory contains data files related to application |
|---|
| | 681 | execution. The file <filename>vrjuggler.plist</filename>, |
|---|
| | 682 | the use of which is highly recommended, must be put in |
|---|
| | 683 | this directory. The application bundle icon file (a file |
|---|
| | 684 | with the extension <filename>.icns</filename>) is also |
|---|
| | 685 | stored in this directory. A default icon file, |
|---|
| | 686 | <filename>vrjuggler.icns</filename>, can be used, or a |
|---|
| | 687 | custom one can be made. The file to use must be named in |
|---|
| | 688 | <filename>Contents/Info.plist</filename>.</para> |
|---|
| | 689 | </section> |
|---|
| | 690 | |
|---|
| | 691 | <section> |
|---|
| | 692 | <title><filename>vrjuggler.plist</filename></title> |
|---|
| | 693 | |
|---|
| | 694 | <para>For VR Juggler application bundles, a useful file |
|---|
| | 695 | is <filename>vrjuggler.plist</filename>. The default |
|---|
| | 696 | version of this file from |
|---|
| | 697 | <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename> |
|---|
| | 698 | disables VR Juggler configuration file loading through |
|---|
| | 699 | <classname>NSApplication</classname> channels and |
|---|
| | 700 | identifies the <classname>NSApplication</classname> |
|---|
| | 701 | delegate class type that will be used (see <xref |
|---|
| | 702 | linkend="section.custom.delegate" />). These are set |
|---|
| | 703 | using the properties <literal>VRJConfigHandling</literal> |
|---|
| | 704 | and <literal>VRJDelegateClass</literal> |
|---|
| | 705 | respectively.</para> |
|---|
| | 706 | |
|---|
| | 707 | <para>The <quote>configuration file loading through |
|---|
| | 708 | <classname>NSApplication</classname> channels</quote> bit |
|---|
| | 709 | has to do with associating VR Juggler configuration files |
|---|
| | 710 | with application bundles. There are different means by |
|---|
| | 711 | which data can be delivered to applications on Mac OS X. |
|---|
| | 712 | For example, double-clicking on a file in the |
|---|
| | 713 | <application>Finder</application> (or selecting |
|---|
| | 714 | <guimenuitem>Open With</guimenuitem> in the context menu |
|---|
| | 715 | for the file) causes a registered handler application to |
|---|
| | 716 | be opened. The file is given to the application once it |
|---|
| | 717 | has opened through the |
|---|
| | 718 | <classname>NSApplication</classname> event system. By |
|---|
| | 719 | setting the <literal>VRJConfigHandling</literal> property |
|---|
| | 720 | to <literal>false</literal> in |
|---|
| | 721 | <filename>Contents/Resources/vrjuggler.plist</filename>, |
|---|
| | 722 | this capability is disabled for the application bundle in |
|---|
| | 723 | question. That is, the application bundle will ignore the |
|---|
| | 724 | <classname>NSApplication</classname> events pertaining to |
|---|
| | 725 | configuration files to be loaded as a result of |
|---|
| | 726 | double-clicking on the said files. Configuration files |
|---|
| | 727 | can still be opened on the fly using the |
|---|
| | 728 | <guimenu>File</guimenu> menu defined in the default NIB |
|---|
| | 729 | (see below).</para> |
|---|
| | 730 | </section> |
|---|
| | 731 | |
|---|
| | 732 | <section> |
|---|
| | 733 | <title><filename>en.lproj</filename> Directory</title> |
|---|
| | 734 | |
|---|
| | 735 | <para>The <filename>en.lproj</filename> subdirectory of |
|---|
| | 736 | <filename>Contents/Resources</filename> contains |
|---|
| | 737 | information translated in the English language. The most |
|---|
| | 738 | important item in this directory is MainMenu.nib |
|---|
| | 739 | (discussed next), but the optional file |
|---|
| | 740 | <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle/InfoPlist.strings</filename>, |
|---|
| | 741 | or some other version of same, can be copied into this |
|---|
| | 742 | directory. Other language-specific directories can be |
|---|
| | 743 | created as subdirectories of |
|---|
| | 744 | <filename>Contents/Resources</filename> as necessary. |
|---|
| | 745 | They, too, must contain a |
|---|
| | 746 | <filename>MainMenu.nib</filename>.</para> |
|---|
| | 747 | </section> |
|---|
| | 748 | |
|---|
| | 749 | <section> |
|---|
| | 750 | <title><filename>MainMenu.nib</filename></title> |
|---|
| | 751 | |
|---|
| | 752 | <para>This is the NIB for the VR juggler application |
|---|
| | 753 | bundle. It has been designed using Interface Builder with |
|---|
| | 754 | a simple user interface that knows how to interact with a |
|---|
| | 755 | VR Juggler application. The interface includes the |
|---|
| | 756 | <guimenu>File</guimenu> menu with the item for opening VR |
|---|
| | 757 | Juggler configuration files on the fly. Other menus and |
|---|
| | 758 | menu items can be added by making a custom MainMenu.nib. |
|---|
| | 759 | Using the version from |
|---|
| | 760 | <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename> |
|---|
| | 761 | as a starting point is a good idea. Note that |
|---|
| | 762 | <filename>MainMenu.nib</filename> is a directory, and it |
|---|
| | 763 | should be copied recursively to |
|---|
| | 764 | <filename>Contents/Resources/en.lproj</filename>.</para> |
|---|
| | 765 | </section> |
|---|
| | 766 | </section> |
|---|
| | 767 | |
|---|
| | 768 | <section id="section.cocoa.app.execution"> |
|---|
| | 769 | <title>Application Execution</title> |
|---|
| | 770 | |
|---|
| | 771 | <para>The <function>main()</function> function |
|---|
| | 772 | implementation show in <xref |
|---|
| | 773 | linkend="section.app.main.structure" /> will work without |
|---|
| | 774 | any problems on Mac OS X. However, many of the VR Juggler |
|---|
| | 775 | sample and test applications have a slightly more |
|---|
| | 776 | complicated <function>main()</function> function. In |
|---|
| | 777 | particular, these applications usually determine whether the |
|---|
| | 778 | user has passed in any arguments through the command line |
|---|
| | 779 | and exit with an error message explaining how to run the |
|---|
| | 780 | application if none were given. The example below shows how |
|---|
| | 781 | this is commonly done with an <literal>if</literal> |
|---|
| | 782 | statement before anything else:</para> |
|---|
| | 783 | |
|---|
| | 784 | <programlisting linenumbering="numbered">@include <iostream> |
|---|
| | 785 | #include <cstdlib> |
|---|
| | 786 | #include <vrj/Kernel/Kernel.h> |
|---|
| | 787 | #include <simpleApp.h> |
|---|
| | 788 | |
|---|
| | 789 | int main(int argc, char* argv[]) |
|---|
| | 790 | { |
|---|
| | 791 | if ( argc <= 1 ) |
|---|
| | 792 | { |
|---|
| | 793 | std::cout << "Usage: " << argv[0] |
|---|
| | 794 | << "cfgfile[0] cfgfile[1] ... cfgfile[n]" |
|---|
| | 795 | << std::endl; |
|---|
| | 796 | std::exit(EXIT_FAILURE); |
|---|
| | 797 | } |
|---|
| | 798 | |
|---|
| | 799 | vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel |
|---|
| | 800 | simpleApp* app = new simpleApp(); // Create the app object |
|---|
| | 801 | |
|---|
| | 802 | kernel->loadConfigFile(...); // Configure the kernel |
|---|
| | 803 | kernel->start(); // Start the kernel thread |
|---|
| | 804 | kernel->setApplication(app); // Give application to kernel |
|---|
| | 805 | kernel->waitForKernelStop(); // Block until kernel stops |
|---|
| | 806 | |
|---|
| | 807 | return 0; |
|---|
| | 808 | }</programlisting> |
|---|
| | 809 | |
|---|
| | 810 | <para>This approach will not necessarily work on Mac OS X |
|---|
| | 811 | because users normally expect to be able to launch an |
|---|
| | 812 | application by double-clicking on its icon in the |
|---|
| | 813 | <application>Finder</application> and then loading data into |
|---|
| | 814 | it through a GUI. If a user launched the above application |
|---|
| | 815 | that way, the application would exit immediately, and the |
|---|
| | 816 | user would have to open the |
|---|
| | 817 | <application>Console</application> application to find out |
|---|
| | 818 | what went wrong. Launching from the command line, while |
|---|
| | 819 | perfectly valid on Mac OS X, is simply not what users |
|---|
| | 820 | expect. At the same time, remote launching of VR juggler |
|---|
| | 821 | applications in a cluster will almost certainly require |
|---|
| | 822 | launching without the use of the |
|---|
| | 823 | <application>Finder</application> and passing in arguments |
|---|
| | 824 | through <varname>argv</varname>. This is definitely the case |
|---|
| | 825 | when using <ulink |
|---|
| | 826 | url="https://realityforge.vrsource.org/trac/maestro/">Maestro</ulink>.</para> |
|---|
| | 827 | |
|---|
| | 828 | <para>If a VR Juggler application will be run on Mac OS X, |
|---|
| | 829 | the programmer has to decide whether command line processing |
|---|
| | 830 | is critical to the execution of the application. There are |
|---|
| | 831 | three options available to VR Juggler programmers on Mac OS |
|---|
| | 832 | X: rely exclusively on command line arguments to launch, |
|---|
| | 833 | handle no command line arguments, or allow the use of |
|---|
| | 834 | command line arguments but do not require them. Given that |
|---|
| | 835 | VR Juggler applications written on all other platforms are |
|---|
| | 836 | highly likely to rely exclusively on command line arguments |
|---|
| | 837 | to launch, the first choice is expected to be the most |
|---|
| | 838 | commonly chosen approach. Nevertheless, we will explain all |
|---|
| | 839 | three.</para> |
|---|
| | 840 | |
|---|
| | 841 | <section> |
|---|
| | 842 | <title>Relying Exclusively on Command Line Arguments to |
|---|
| | 843 | Launch</title> |
|---|
| | 844 | |
|---|
| | 845 | <para>If a VR Juggler application used on Mac OS X will |
|---|
| | 846 | rely solely on command line arguments to launch, it is |
|---|
| | 847 | operating in exactly the same manner as on all other |
|---|
| | 848 | platforms. The <function>main()</function> function can |
|---|
| | 849 | be written as is shown in this document to exit if no |
|---|
| | 850 | command line arguments are supplied. There is still one |
|---|
| | 851 | more thing to do, though. In the |
|---|
| | 852 | <filename>Resources</filename> directory of the |
|---|
| | 853 | application bundle, there must be a property list file in |
|---|
| | 854 | the bundle called |
|---|
| | 855 | <filename>Contents/Resources/vrjuggler.plist</filename>. |
|---|
| | 856 | This property list must have the property |
|---|
| | 857 | <literal>VRJConfigHandling</literal> set to |
|---|
| | 858 | <literal>false</literal>. This is the default setting for |
|---|
| | 859 | this property if the <filename>vrjuggler.plist</filename> |
|---|
| | 860 | file that comes with VR Juggler was used in constructing |
|---|
| | 861 | the application bundle.</para> |
|---|
| | 862 | |
|---|
| | 863 | <para>To execute the application, run the program in |
|---|
| | 864 | <filename><appname>.app/Contents/MacOS</filename> |
|---|
| | 865 | from a command prompt and pass in the command line |
|---|
| | 866 | arguments. The appropriate event handling will be set up |
|---|
| | 867 | so that the application will behave just as any other Mac |
|---|
| | 868 | OS X application.</para> |
|---|
| | 869 | </section> |
|---|
| | 870 | |
|---|
| | 871 | <section> |
|---|
| | 872 | <title>Handling No Command Line Arguments at Launch |
|---|
| | 873 | Time</title> |
|---|
| | 874 | |
|---|
| | 875 | <para>If no command line arguments are to be handled |
|---|
| | 876 | (i.e., the application is to behave the same as typical |
|---|
| | 877 | Mac OS X applications), then the |
|---|
| | 878 | <function>main()</function> function shown above needs to |
|---|
| | 879 | change. Specifically, it cannot exit with an error |
|---|
| | 880 | message if no command line arguments are passed in. To |
|---|
| | 881 | keep the application portable, a preprocessor conditional |
|---|
| | 882 | can be used, as shown in <xref |
|---|
| | 883 | linkend="example.cocoa.no.cmd.flags" />. Then, in |
|---|
| | 884 | <filename>Contents/Resources/vrjuggler.plist</filename>, |
|---|
| | 885 | set the <literal>VRJConfigHandling</literal> property to |
|---|
| | 886 | <literal>true</literal>. To execute the application, |
|---|
| | 887 | double-click the application bundle icon in the |
|---|
| | 888 | <application>Finder</application> or use the |
|---|
| | 889 | <command>open</command> command from a command |
|---|
| | 890 | prompt.</para> |
|---|
| | 891 | |
|---|
| | 892 | <example id="example.cocoa.no.cmd.flags"> |
|---|
| | 893 | <title>Ignoring Command Line Arguments on Mac OS |
|---|
| | 894 | X</title> |
|---|
| | 895 | |
|---|
| | 896 | <programlisting linenumbering="numbered">@include <iostream> |
|---|
| | 897 | #include <cstdlib> |
|---|
| | 898 | #include <vrj/Kernel/Kernel.h> |
|---|
| | 899 | #include <simpleApp.h> |
|---|
| | 900 | |
|---|
| | 901 | int main(int argc, char* argv[]) |
|---|
| | 902 | { |
|---|
| | 903 | #if ! defined(VRJ_USE_COCOA) |
|---|
| | 904 | if ( argc <= 1 ) |
|---|
| | 905 | { |
|---|
| | 906 | std::cout << "Usage: " << argv[0] |
|---|
| | 907 | << "cfgfile[0] cfgfile[1] ... cfgfile[n]" |
|---|
| | 908 | << std::endl; |
|---|
| | 909 | std::exit(EXIT_FAILURE); |
|---|
| | 910 | } |
|---|
| | 911 | #endif |
|---|
| | 912 | |
|---|
| | 913 | vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel |
|---|
| | 914 | simpleApp* app = new simpleApp(); // Create the app object |
|---|
| | 915 | |
|---|
| | 916 | #if ! defined(VRJ_USE_COCOA) |
|---|
| | 917 | kernel->loadConfigFile(...); // Configure the kernel |
|---|
| | 918 | #endif |
|---|
| | 919 | kernel->start(); // Start the kernel thread |
|---|
| | 920 | kernel->setApplication(app); // Give application to kernel |
|---|
| | 921 | kernel->waitForKernelStop(); // Block until kernel stops |
|---|
| | 922 | |
|---|
| | 923 | return 0; |
|---|
| | 924 | }</programlisting> |
|---|
| | 925 | </example> |
|---|
| | 926 | </section> |
|---|
| | 927 | |
|---|
| | 928 | <section> |
|---|
| | 929 | <title>Allowing Optional Use of Command Line |
|---|
| | 930 | Arguments</title> |
|---|
| | 931 | |
|---|
| | 932 | <para>A compromise can be struck between the previous two |
|---|
| | 933 | options by allowing optional use of command line |
|---|
| | 934 | arguments. The compromise is simple: do not require |
|---|
| | 935 | command line arguments but still handle them if they are |
|---|
| | 936 | given. The form of the main() function that allows this |
|---|
| | 937 | is shown in <xref |
|---|
| | 938 | linkend="example.cocoa.optional.cmd.flags" />. Note that |
|---|
| | 939 | the <literal>#if</literal> around the call to |
|---|
| | 940 | <methodname>vrj::Kernel::loadConfigFile()</methodname> |
|---|
| | 941 | has been removed.</para> |
|---|
| | 942 | |
|---|
| | 943 | <example id="example.cocoa.optional.cmd.flags"> |
|---|
| | 944 | <title>Handling Optional Command Line Arguments on Mac |
|---|
| | 945 | OS X</title> |
|---|
| | 946 | |
|---|
| | 947 | <programlisting linenumbering="numbered">@include <iostream> |
|---|
| | 948 | #include <cstdlib> |
|---|
| | 949 | #include <vrj/Kernel/Kernel.h> |
|---|
| | 950 | #include <simpleApp.h> |
|---|
| | 951 | |
|---|
| | 952 | int main(int argc, char* argv[]) |
|---|
| | 953 | { |
|---|
| | 954 | #if ! defined(VRJ_USE_COCOA) |
|---|
| | 955 | if ( argc <= 1 ) |
|---|
| | 956 | { |
|---|
| | 957 | std::cout << "Usage: " << argv[0] |
|---|
| | 958 | << "cfgfile[0] cfgfile[1] ... cfgfile[n]" |
|---|
| | 959 | << std::endl; |
|---|
| | 960 | std::exit(EXIT_FAILURE); |
|---|
| | 961 | } |
|---|
| | 962 | #endif |
|---|
| | 963 | |
|---|
| | 964 | vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel |
|---|
| | 965 | simpleApp* app = new simpleApp(); // Create the app object |
|---|
| | 966 | |
|---|
| | 967 | kernel->loadConfigFile(...); // Configure the kernel |
|---|
| | 968 | kernel->start(); // Start the kernel thread |
|---|
| | 969 | kernel->setApplication(app); // Give application to kernel |
|---|
| | 970 | kernel->waitForKernelStop(); // Block until kernel stops |
|---|
| | 971 | |
|---|
| | 972 | return 0; |
|---|
| | 973 | }</programlisting> |
|---|
| | 974 | </example> |
|---|
| | 975 | |
|---|
| | 976 | <para>The only decision left to make is what value to use |
|---|
| | 977 | for the <literal>VRJConfigHandling</literal> property in |
|---|
| | 978 | <filename>Contents/Resources/vrjuggler.plist</filename>. |
|---|
| | 979 | If we set it to <filename>false</filename> (recall that |
|---|
| | 980 | that is the default in the |
|---|
| | 981 | <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle/vrjuggler.plist</filename> |
|---|
| | 982 | version), the application cannot be used as a handler for |
|---|
| | 983 | VR Juggler configuration files. If we set it to true, we |
|---|
| | 984 | have to be prepared for configuration files to come in |
|---|
| | 985 | other than through the command line or through the use of |
|---|
| | 986 | the <guimenu>File</guimenu> menu. The default |
|---|
| | 987 | <classname>NSApplication</classname> delegate |
|---|
| | 988 | (<classname>VRJBasicDelegate</classname>) can deal with |
|---|
| | 989 | either case. The default behavior for VR Juggler sample |
|---|
| | 990 | applications is to allow configuration files to be |
|---|
| | 991 | specified on the command line through the use of a |
|---|
| | 992 | <function>main()</function> function similar to that |
|---|
| | 993 | shown in <xref |
|---|
| | 994 | linkend="example.cocoa.optional.cmd.flags" /> and to |
|---|
| | 995 | leave the property <literal>VRJConfigHandling</literal> |
|---|
| | 996 | in |
|---|
| | 997 | <filename>Contents/Resources/vrjuggler.plist</filename> |
|---|
| | 998 | set to <literal>false</literal>.</para> |
|---|
| | 999 | </section> |
|---|
| | 1000 | </section> |
|---|
| | 10951 | |
|---|
| | 10952 | <chapter> |
|---|
| | 10953 | <title>Advanced Topics</title> |
|---|
| | 10954 | |
|---|
| | 10955 | <para>In this chapter, we address topics that are considered to be |
|---|
| | 10956 | for advanced uses of VR Juggler. This is not to say that they are |
|---|
| | 10957 | difficult concepts or hard-to-use features. Rather, they are not |
|---|
| | 10958 | things that the average VR Juggler application programmer will do or |
|---|
| | 10959 | even need to be aware of when writing VR software. These are |
|---|
| | 10960 | important, interesting, and worthwhile topics nonetheless.</para> |
|---|
| | 10961 | |
|---|
| | 10962 | <section id="section.custom.delegate"> |
|---|
| | 10963 | <title>Making a Custom <classname>NSApplication</classname> |
|---|
| | 10964 | Delegate on Mac OS X</title> |
|---|
| | 10965 | |
|---|
| | 10966 | <para>The Cocoa support for VR Juggler is designed to take |
|---|
| | 10967 | advantage of the flexibility of Cocoa application design and, more |
|---|
| | 10968 | generally, the flexibility of the Objective-C language. |
|---|
| | 10969 | Specifically, VR Juggler application programmers targeting Mac OS |
|---|
| | 10970 | X have a unique opportunity to customize the behavior of VR |
|---|
| | 10971 | Juggler application behavior. Behind the scenes, |
|---|
| | 10972 | <classname>vrj::CocoaWrapper</classname> registers a delegate, an |
|---|
| | 10973 | instance of the Objective-C class |
|---|
| | 10974 | <classname>VRJBasicDelegate</classname>, with the |
|---|
| | 10975 | <classname>NSApplication</classname> singleton. Messages from |
|---|
| | 10976 | <classname>NSApplication</classname> are received and handled by |
|---|
| | 10977 | this object. VR Juggler application programmers can tailor the |
|---|
| | 10978 | handling of these messages by creating and using a custom |
|---|
| | 10979 | <classname>NSApplication</classname> delegate.</para> |
|---|
| | 10980 | |
|---|
| | 10981 | <para>The full details of delegates and customizing the behavior |
|---|
| | 10982 | of <classname>NSApplication</classname> are well beyond the scope |
|---|
| | 10983 | of this document. Readers are referred to Apple's documentation |
|---|
| | 10984 | for <ulink |
|---|
| | 10985 | url="http://developer.apple.com/referencelibrary/GettingStarted/GS_Cocoa/index.html">Cocoa</ulink> |
|---|
| | 10986 | and <ulink |
|---|
| | 10987 | url="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html"><classname>NSApplication</classname> |
|---|
| | 10988 | class reference</ulink>. We will proceed from here under the |
|---|
| | 10989 | assumption that readers are already familiar with the relevant and |
|---|
| | 10990 | necessary topics including, but not limited to, Objective-C, |
|---|
| | 10991 | Objective-C++, and <classname>NSApplication</classname>.</para> |
|---|
| | 10992 | |
|---|
| | 10993 | <para>In a typical Cocoa application, the delegate would be given |
|---|
| | 10994 | to the <classname>NSApplication</classname> singleton instance by |
|---|
| | 10995 | sending that object the<methodname>-setDelegate:</methodname> |
|---|
| | 10996 | message. Doing so, however, will interfere with the proper |
|---|
| | 10997 | functionality of VR Juggler. Thus, the registration mechanism is |
|---|
| | 10998 | done using the <literal>VRJDelegateClass</literal> property in |
|---|
| | 10999 | <filename>Contents/Resources/vrjuggler.plist</filename>. The |
|---|
| | 11000 | default value of this property is |
|---|
| | 11001 | <classname>VRJBasicDelegate</classname>. By changing this to the |
|---|
| | 11002 | name of the custom class, <classname>vrj::CocoaWrapper</classname> |
|---|
| | 11003 | will instantiate, register, and use the custom class instead. |
|---|
| | 11004 | (Being able to do this is a simple and elegant feature of |
|---|
| | 11005 | Objective-C.)</para> |
|---|
| | 11006 | |
|---|
| | 11007 | <para>There are two ways to create a custom delegate class. The |
|---|
| | 11008 | class can either stand on its own as a subclass of |
|---|
| | 11009 | <classname>NSObject</classname>, or it can derive from |
|---|
| | 11010 | <classname>VRJBasicDelegate</classname>. The decision about which |
|---|
| | 11011 | approach to use depends on whether complete or partial |
|---|
| | 11012 | customization is desired. For this discussion, we will focus on |
|---|
| | 11013 | complete customizationâthat is, creating a new delegate type by |
|---|
| | 11014 | deriving from <classname>NSObject</classname>.</para> |
|---|
| | 11015 | |
|---|
| | 11016 | <sidebar> |
|---|
| | 11017 | <para>It is worth pointing out that |
|---|
| | 11018 | <classname>VRJBasicDelegate</classname> is not necessarily |
|---|
| | 11019 | designed to be extended. It can be customized, but mainly, this |
|---|
| | 11020 | customization is done by overriding methods to |
|---|
| | 11021 | <emphasis>replace</emphasis> the behavior of the base class |
|---|
| | 11022 | rather than to extend it. Refer to the implementation of |
|---|
| | 11023 | <classname>VRJBasicDelegate</classname> to get a better idea of |
|---|
| | 11024 | the implications of overriding it for the purposes of |
|---|
| | 11025 | customization. The code can be found in |
|---|
| | 11026 | <filename>modules/vrjuggler/vrj/Kernel/VRJBasicDelegate.mm</filename> |
|---|
| | 11027 | and |
|---|
| | 11028 | <filename>modules/vrjuggler/vrj/Kernel/VRJBasicDelegate.h</filename>.</para> |
|---|
| | 11029 | </sidebar> |
|---|
| | 11030 | |
|---|
| | 11031 | <para>As noted, the job of the delegate is to respond to messages |
|---|
| | 11032 | from <classname>NSApplication</classname>. The messages that are |
|---|
| | 11033 | delivered to the delegate are documented in the |
|---|
| | 11034 | <classname>NSApplication</classname> reference, and we will not |
|---|
| | 11035 | duplicate that information here. Instead, we will concentrate on a |
|---|
| | 11036 | small subset of these messages related to document loading. The |
|---|
| | 11037 | purpose is twofold. First, this explanation helps in understanding |
|---|
| | 11038 | the implementation of <classname>VRJBasicDelegate</classname>, |
|---|
| | 11039 | thereby clarifying options for customizing the behavior of that |
|---|
| | 11040 | class. Second, document loading is a prominent topic in Mac OS X |
|---|
| | 11041 | application development, and it is likely to be of the most |
|---|
| | 11042 | interest to VR application programmers, too.</para> |
|---|
| | 11043 | |
|---|
| | 11044 | <para>To get things under way, have a glance at <xref |
|---|
| | 11045 | linkend="example.basic.delegate.impl" />. It is a relatively |
|---|
| | 11046 | simple class that uses Objective-C++ capabilities to create a |
|---|
| | 11047 | simple bridge between <filename>NSApplication</filename> and |
|---|
| | 11048 | <classname>vrj::Kernel</classname>. The documents being loaded in |
|---|
| | 11049 | this case are VR Juggler configuration files, but it is not much |
|---|
| | 11050 | of a leap to imagine the use of any other document type (i.e., |
|---|
| | 11051 | data file used as input to the application). We will review each |
|---|
| | 11052 | of the methods of this class.</para> |
|---|
| | 11053 | |
|---|
| | 11054 | <example id="example.basic.delegate.impl"> |
|---|
| | 11055 | <title><filename>MyDelegate.mm</filename>: Basic Delegate |
|---|
| | 11056 | Implementation</title> |
|---|
| | 11057 | |
|---|
| | 11058 | <programlisting linenumbering="numbered">#import <Foundation/NSArray.h> |
|---|
| | 11059 | #import <Foundation/NSString.h> |
|---|
| | 11060 | #import <AppKit/NSApplication.h> |
|---|
| | 11061 | |
|---|
| | 11062 | #include <vrj/Kernel/Kernel.h> |
|---|
| | 11063 | |
|---|
| | 11064 | |
|---|
| | 11065 | @interface MyDelegate : NSObject |
|---|
| | 11066 | { |
|---|
| | 11067 | BOOL mLoadConfigs; |
|---|
| | 11068 | } |
|---|
| | 11069 | @end |
|---|
| | 11070 | |
|---|
| | 11071 | @implementation MyDelegate |
|---|
| | 11072 | -(id) init |
|---|
| | 11073 | { |
|---|
| | 11074 | mLoadConfigs = YES; |
|---|
| | 11075 | return [super init]; |
|---|
| | 11076 | } |
|---|
| | 11077 | |
|---|
| | 11078 | -(void) setLoadConfigs:(BOOL) load |
|---|
| | 11079 | { |
|---|
| | 11080 | mLoadConfigs = load; |
|---|
| | 11081 | } |
|---|
| | 11082 | |
|---|
| | 11083 | -(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication*) sender |
|---|
| | 11084 | { |
|---|
| | 11085 | // We return NO here because we have a different way of shutting down |
|---|
| | 11086 | // the application. When vrj::Kernel::stop() is invoked, it will cause |
|---|
| | 11087 | // the application run loop to stop by invoking |
|---|
| | 11088 | // vrj::CocoaWrapper::stop(). |
|---|
| | 11089 | return NO; |
|---|
| | 11090 | } |
|---|
| | 11091 | |
|---|
| | 11092 | -(BOOL) application:(NSApplication*) theApplication |
|---|
| | 11093 | openFile:(NSString*) file |
|---|
| | 11094 | { |
|---|
| | 11095 | if ( mLoadConfigs ) |
|---|
| | 11096 | { |
|---|
| | 11097 | vrj::Kernel::instance()->loadConfigFile([file UTF8String]); |
|---|
| | 11098 | } |
|---|
| | 11099 | |
|---|
| | 11100 | return YES; |
|---|
| | 11101 | } |
|---|
| | 11102 | |
|---|
| | 11103 | -(void) application:(NSApplication*) theApplication |
|---|
| | 11104 | openFiles:(NSArray*) files |
|---|
| | 11105 | { |
|---|
| | 11106 | if ( mLoadConfigs ) |
|---|
| | 11107 | { |
|---|
| | 11108 | const int count = [files count]; |
|---|
| | 11109 | for ( int i = 0; i < count; ++i ) |
|---|
| | 11110 | { |
|---|
| | 11111 | NSString* file = [files objectAtIndex:i]; |
|---|
| | 11112 | vrj::Kernel::instance()->loadConfigFile([file UTF8String]); |
|---|
| | 11113 | } |
|---|
| | 11114 | } |
|---|
| | 11115 | } |
|---|
| | 11116 | @end</programlisting> |
|---|
| | 11117 | </example> |
|---|
| | 11118 | |
|---|
| | 11119 | <section> |
|---|
| | 11120 | <title>Data Members</title> |
|---|
| | 11121 | |
|---|
| | 11122 | <para>The class <classname>MyDelegate</classname> has a single |
|---|
| | 11123 | data member, <varname>mLoadConfigs</varname>, that determines |
|---|
| | 11124 | how instances of this class will respond to certain messages |
|---|
| | 11125 | sent by <classname>NSApplication</classname>. The value will be |
|---|
| | 11126 | changed if <classname>vrj::CocoaWrapper</classname> sends the |
|---|
| | 11127 | <methodname>-setLoadConfigs:</methodname> message as a result |
|---|
| | 11128 | of the property <literal>VRJConfigHandling</literal> being set |
|---|
| | 11129 | in |
|---|
| | 11130 | <filename>Contents/Resources/vrjuggler.plist</filename>.</para> |
|---|
| | 11131 | </section> |
|---|
| | 11132 | |
|---|
| | 11133 | <section> |
|---|
| | 11134 | <title>Designated Initializer</title> |
|---|
| | 11135 | |
|---|
| | 11136 | <para>Every <classname>NSObject</classname> subclass has a |
|---|
| | 11137 | designated initializer. In the case of the VR Juggler |
|---|
| | 11138 | <classname>NSApplication</classname> delegate, this is the |
|---|
| | 11139 | basic <methodname>-init</methodname> method. The current usage |
|---|
| | 11140 | of the application delegate requires that this method be the |
|---|
| | 11141 | designated initializer, meaning that it is the only initializer |
|---|
| | 11142 | that will be invoked upon creating the delegate instance. This |
|---|
| | 11143 | limitation may be fixed in a future version of |
|---|
| | 11144 | <classname>vrj::CocoaWrapper</classname>. If the custom |
|---|
| | 11145 | delegate does not accept the -init message, then the inherited |
|---|
| | 11146 | <classname>NSObject</classname> version will be used.</para> |
|---|
| | 11147 | |
|---|
| | 11148 | <para>In the meantime, this is where all object initialization |
|---|
| | 11149 | takes place. What this means exactly is up to the delegate |
|---|
| | 11150 | author. In this case, we set the data member |
|---|
| | 11151 | <varname>mLoadConfigs</varname> to <constant>YES</constant>, |
|---|
| | 11152 | send our base class the <methodname>-init</methodname> message, |
|---|
| | 11153 | and return the result. This last step (a widely used |
|---|
| | 11154 | convention) is what must be done by any custom delegate |
|---|
| | 11155 | implementation of <methodname>-init</methodname>.</para> |
|---|
| | 11156 | </section> |
|---|
| | 11157 | |
|---|
| | 11158 | <section> |
|---|
| | 11159 | <title><methodname>-setLoadConfigs:</methodname></title> |
|---|
| | 11160 | |
|---|
| | 11161 | <para>This method is a unique aspect of the delegate as it is |
|---|
| | 11162 | handled by <classname>vrj::CocoaWrapper</classname>. |
|---|
| | 11163 | Specifically, <classname>vrj::CocoaWrapper</classname> will |
|---|
| | 11164 | examine <filename>Contents/Resources/vrjuggler.plist</filename> |
|---|
| | 11165 | to see if it contains the property |
|---|
| | 11166 | <literal>VRJConfigHandling</literal>. If it does, it will send |
|---|
| | 11167 | this message to the delegate instance with the value as it is |
|---|
| | 11168 | set in the property list. This happens before the delegate |
|---|
| | 11169 | object is handed off to the |
|---|
| | 11170 | <classname>NSApplication</classname> singleton. The |
|---|
| | 11171 | consequences of setting and using the |
|---|
| | 11172 | <literal>VRJConfigHandling</literal> property were described in |
|---|
| | 11173 | <xref linkend="section.cocoa.app.execution" />.</para> |
|---|
| | 11174 | |
|---|
| | 11175 | <para>A custom delegate does not have to implement this method. |
|---|
| | 11176 | If it does not, the Objective-C runtime will print a warning on |
|---|
| | 11177 | the console stating that the object does not receive this |
|---|
| | 11178 | message. (A future version of |
|---|
| | 11179 | <classname>vrj::CocoaWrapper</classname> may examine the |
|---|
| | 11180 | delegate interface to determine if it receives the message just |
|---|
| | 11181 | to avoid having that message printed.) It is up to the delegate |
|---|
| | 11182 | author to determine if implementing this method is necessary |
|---|
| | 11183 | and, if so, how it should behave. In our example, we set |
|---|
| | 11184 | <varname>mLoadConfigs</varname> to the value of the parameter, |
|---|
| | 11185 | just as <classname>VRJBasicDelegate</classname> does.</para> |
|---|
| | 11186 | </section> |
|---|
| | 11187 | |
|---|
| | 11188 | <section> |
|---|
| | 11189 | <title><methodname>-applicationShouldTerminateAfterLastWindowClosed:</methodname></title> |
|---|
| | 11190 | |
|---|
| | 11191 | <para>This method is used by |
|---|
| | 11192 | <classname>NSApplication</classname> to allow customization of |
|---|
| | 11193 | application shutdown. In the case of an application delegate |
|---|
| | 11194 | instantiated and used by |
|---|
| | 11195 | <classname>vrj::CocoaWrapper</classname>, it is critical that |
|---|
| | 11196 | this method return the value <constant>NO</constant>. The VR |
|---|
| | 11197 | Juggler kernel knows about |
|---|
| | 11198 | <classname>vrj::CocoaWrapper</classname> and knows how to shut |
|---|
| | 11199 | it down when the time is right. Because of that, we wan |
|---|