2
0
Эх сурвалжийг харах

make a mac-friendly application bundle for standalone panda3d

David Rose 16 жил өмнө
parent
commit
bd79880ee1

+ 11 - 10
direct/src/plugin_npapi/make_osx_bundle.py

@@ -15,7 +15,7 @@ import glob
 import shutil
 
 import direct
-from pandac.PandaModules import Filename
+from pandac.PandaModules import Filename, DSearchPath
 
 def usage(code, msg = ''):
     print >> sys.stderr, __doc__
@@ -25,14 +25,15 @@ def usage(code, msg = ''):
 def makeBundle(startDir):
     fstartDir = Filename.fromOsSpecific(startDir)
 
-    # First, make sure there is only one Opt?-* directory, to avoid
-    # ambiguity.
-    optDirs = glob.glob(fstartDir.toOsSpecific() + '/Opt?-*')
-    if len(optDirs) == 0:
-        raise StandardError, 'Application has not yet been compiled.'
-    if len(optDirs) > 1:
-        raise StandardError, 'Too many compiled directories; ambiguous.'
-    optDir = optDirs[0]
+    # Search for nppandad along $DYLD_LIBRARY_PATH.
+    path = DSearchPath()
+    if 'LD_LIBRARY_PATH' in os.environ:
+        path.appendPath(os.environ['LD_LIBRARY_PATH'])
+    if 'DYLD_LIBRARY_PATH' in os.environ:
+        path.appendPath(os.environ['DYLD_LIBRARY_PATH'])
+    nppanda3d = path.findFile('nppanda3d')
+    if not nppanda3d:
+        raise StandardError, "Couldn't find nppanda3d on path."
 
     # Generate the bundle directory structure
     rootFilename = Filename(fstartDir, 'bundle')
@@ -53,7 +54,7 @@ def makeBundle(startDir):
 
     # Copy in Info.plist and the compiled executable.
     shutil.copyfile(Filename(fstartDir, "nppanda3d.plist").toOsSpecific(), plistFilename.toOsSpecific())
-    shutil.copyfile(optDir + '/nppanda3d', exeFilename.toOsSpecific())
+    shutil.copyfile(nppanda3d.toOsSpecific(), exeFilename.toOsSpecific())
 
     # All done!
     bundleFilename.touch()

+ 29 - 3
direct/src/plugin_standalone/Sources.pp

@@ -19,7 +19,8 @@
   #define OSX_SYS_FRAMEWORKS Foundation AppKit Carbon
 
   #define SOURCES \
-    panda3d.cxx panda3d.h panda3d.I
+    panda3d.cxx panda3d.h panda3d.I \
+    panda3dMain.cxx
 
   #define WIN_SYS_LIBS user32.lib gdi32.lib shell32.lib ole32.lib
 
@@ -32,7 +33,6 @@
   #define BUILD_TARGET $[WINDOWS_PLATFORM]
   #define USE_PACKAGES openssl zlib
   #define TARGET panda3dw
-  #define EXTRA_CDEFS NON_CONSOLE
 
   #define LOCAL_LIBS plugin_common
 
@@ -45,8 +45,34 @@
   #define OSX_SYS_FRAMEWORKS Foundation AppKit Carbon
 
   #define SOURCES \
-    panda3d.cxx panda3d.h panda3d.I
+    panda3d.cxx panda3d.h panda3d.I \
+    panda3dWinMain.cxx
 
   #define WIN_SYS_LIBS user32.lib gdi32.lib shell32.lib ole32.lib
 
 #end bin_target
+
+#begin bin_target
+  // On Mac, we'll build panda3d_mac, which is the Carbon-friendly
+  // application we wrap in a bundle, for picking a p3d file from
+  // Finder.
+
+  #define BUILD_TARGET $[OSX_PLATFORM]
+  #define USE_PACKAGES openssl zlib
+  #define TARGET panda3d_mac
+
+  #define LOCAL_LIBS plugin_common
+
+  #define OTHER_LIBS \
+    prc:c dtoolutil:c dtoolbase:c dtool:m \
+    interrogatedb:c dconfig:c dtoolconfig:m \
+    downloader:c express:c pandaexpress:m \
+    pystub
+
+  #define OSX_SYS_FRAMEWORKS Foundation AppKit Carbon
+
+  #define SOURCES \
+    panda3d.cxx panda3d.h panda3d.I \
+    panda3dMac.cxx panda3dMac.h panda3dMac.I
+
+#end bin_target

+ 12 - 1
direct/src/plugin_standalone/panda3d.I

@@ -13,6 +13,18 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::time_to_exit
+//       Access: Public
+//  Description: Returns true if it is time to exit because the last
+//               instance has exited, or false if we should continue
+//               running.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+time_to_exit() {
+  return _instances.empty() && _exit_with_last_instance;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::URLGetter::get_instance
 //       Access: Public
@@ -23,4 +35,3 @@ P3D_instance *Panda3D::URLGetter::
 get_instance() {
   return _instance;
 }
-

+ 208 - 250
direct/src/plugin_standalone/panda3d.cxx

@@ -50,24 +50,38 @@ static const double wait_cycle = 0.2;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 Panda3D::
-Panda3D() {
+Panda3D(bool console_environment) {
+  _console_environment = console_environment;
+
   _root_dir = find_root_dir();
   _reporting_download = false;
   _enable_security = false;
 
+  _window_type = P3D_WT_toplevel;
+
+  _win_x = 0;
+  _win_y = 0;
+  _win_width = 640;
+  _win_height = 480;
+
+  _exit_with_last_instance = true;
+  _host_url = PANDA_PACKAGE_HOST_URL;
+  _this_platform = DTOOL_PLATFORM;
+  _verify_contents = false;
+
   // Seed the lame random number generator in rand(); we use it to
   // select a mirror for downloading.
   srand((unsigned int)time(NULL));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::run
+//     Function: Panda3D::run_command_line
 //       Access: Public
-//  Description: Starts the program going.  Returns 0 on success,
-//               nonzero on failure.
+//  Description: Starts the program going, with command-line arguments.  
+//               Returns 0 on success, nonzero on failure.
 ////////////////////////////////////////////////////////////////////
 int Panda3D::
-run(int argc, char *argv[]) {
+run_command_line(int argc, char *argv[]) {
   extern char *optarg;
   extern int optind;
 
@@ -77,14 +91,6 @@ run(int argc, char *argv[]) {
   const char *optstr = "+mu:M:Sp:fw:t:s:o:l:ih";
 
   bool allow_multiple = false;
-  _host_url = PANDA_PACKAGE_HOST_URL;
-  string super_mirror_url;
-  _this_platform = DTOOL_PLATFORM;
-  _verify_contents = false;
-
-  P3D_window_type window_type = P3D_WT_toplevel;
-  int win_x = 0, win_y = 0;
-  int win_width = 640, win_height = 480;
 
   int flag = getopt(argc, argv, optstr);
 
@@ -99,7 +105,7 @@ run(int argc, char *argv[]) {
       break;
 
     case 'M':
-      super_mirror_url = optarg;
+      _super_mirror_url = optarg;
       break;
 
     case 'S':
@@ -116,13 +122,13 @@ run(int argc, char *argv[]) {
 
     case 'w':
       if (strcmp(optarg, "toplevel") == 0) {
-        window_type = P3D_WT_toplevel;
+        _window_type = P3D_WT_toplevel;
       } else if (strcmp(optarg, "embedded") == 0) {
-        window_type = P3D_WT_embedded;
+        _window_type = P3D_WT_embedded;
       } else if (strcmp(optarg, "fullscreen") == 0) {
-        window_type = P3D_WT_fullscreen;
+        _window_type = P3D_WT_fullscreen;
       } else if (strcmp(optarg, "hidden") == 0) {
-        window_type = P3D_WT_hidden;
+        _window_type = P3D_WT_hidden;
       } else {
         cerr << "Invalid value for -w: " << optarg << "\n";
         return 1;
@@ -137,14 +143,14 @@ run(int argc, char *argv[]) {
       break;
 
     case 's':
-      if (!parse_int_pair(optarg, win_width, win_height)) {
+      if (!parse_int_pair(optarg, _win_width, _win_height)) {
         cerr << "Invalid value for -s: " << optarg << "\n";
         return 1;
       }
       break;
 
     case 'o':
-      if (!parse_int_pair(optarg, win_x, win_y)) {
+      if (!parse_int_pair(optarg, _win_x, _win_y)) {
         cerr << "Invalid value for -o: " << optarg << "\n";
         return 1;
       }
@@ -193,127 +199,111 @@ run(int argc, char *argv[]) {
   argc -= (optind-1);
   argv += (optind-1);
 
-  if (argc < 2) {
+  if (argc < 2 && _exit_with_last_instance) {
+    // No instances on the command line--that *might* be an error.
     usage();
     return 1;
   }
 
-  // Set host_url_prefix to end with a slash.
-  _host_url_prefix = _host_url;
-  if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.length() - 1] != '/') {
-    _host_url_prefix += '/';
-  }
-  _download_url_prefix = _host_url_prefix;
-
-  // If the "super mirror" URL is a filename, convert it to a file:// url.
-  if (!super_mirror_url.empty()) {
-    if (!is_url(super_mirror_url)) {
-      Filename filename = Filename::from_os_specific(super_mirror_url);
-      filename.make_absolute();
-      string path = filename.to_os_generic();
-      if (!path.empty() && path[0] != '/') {
-        // On Windows, a leading drive letter must be preceded by an
-        // additional slash.
-        path = "/" + path;
-      }
-      super_mirror_url = "file://" + path;
-    }
-
-    // And make sure the super_mirror_url_prefix ends with a slash.
-    _super_mirror_url_prefix = super_mirror_url;
-    if (!_super_mirror_url_prefix.empty() && _super_mirror_url_prefix[_super_mirror_url_prefix.length() - 1] != '/') {
-      _super_mirror_url_prefix += '/';
-    }
-  }
-
-  if (!get_plugin()) {
-    cerr << "Unable to load Panda3D plugin.\n";
+  if (!post_arg_processing()) {
     return 1;
   }
 
-  // Set up the "super mirror" URL, if specified.
-  if (!super_mirror_url.empty()) {
-    P3D_set_super_mirror(super_mirror_url.c_str());
-  }
-
   int num_instance_filenames, num_instance_args;
   char **instance_filenames, **instance_args;
 
-  if (allow_multiple) {
-    // With -m, the remaining arguments are all instance filenames.
-    num_instance_filenames = argc - 1;
-    instance_filenames = argv + 1;
-    num_instance_args = 0;
-    instance_args = argv + argc;
-
-  } else {
-    // Without -m, there is one instance filename, and everything else
-    // gets delivered to that instance.
-    num_instance_filenames = 1;
-    instance_filenames = argv + 1;
-    num_instance_args = argc - 2;
-    instance_args = argv + 2;
-  }
-
-  P3D_window_handle parent_window;
-  if (window_type == P3D_WT_embedded) {
-    // The user asked for an embedded window.  Create a toplevel
-    // window to be its parent, of the requested size.
-    if (win_width == 0 && win_height == 0) {
-      win_width = 640;
-      win_height = 480;
+  if (argc > 1) {
+    if (allow_multiple) {
+      // With -m, the remaining arguments are all instance filenames.
+      num_instance_filenames = argc - 1;
+      instance_filenames = argv + 1;
+      num_instance_args = 0;
+      instance_args = argv + argc;
+      
+    } else {
+      // Without -m, there is one instance filename, and everything else
+      // gets delivered to that instance.
+      num_instance_filenames = 1;
+      instance_filenames = argv + 1;
+      num_instance_args = argc - 2;
+      instance_args = argv + 2;
     }
-
-    make_parent_window(parent_window, win_width, win_height);
     
-    // Center the child window(s) within the parent window.
+    if (_window_type == P3D_WT_embedded) {
+      // The user asked for an embedded window.  Create a toplevel
+      // window to be its parent, of the requested size.
+      if (_win_width == 0 && _win_height == 0) {
+        _win_width = 640;
+        _win_height = 480;
+      }
+      
+      make_parent_window();
+      
+      // Center the child window(s) within the parent window.
 #ifdef _WIN32
-    RECT rect;
-    GetClientRect(parent_window._hwnd, &rect);
-
-    win_x = (int)(rect.right * 0.1);
-    win_y = (int)(rect.bottom * 0.1);
-    win_width = (int)(rect.right * 0.8);
-    win_height = (int)(rect.bottom * 0.8);
+      RECT rect;
+      GetClientRect(_parent_window._hwnd, &rect);
+      
+      _win_x = (int)(rect.right * 0.1);
+      _win_y = (int)(rect.bottom * 0.1);
+      _win_width = (int)(rect.right * 0.8);
+      _win_height = (int)(rect.bottom * 0.8);
 #endif
-
-    // Subdivide the window into num_x_spans * num_y_spans sub-windows.
-    int num_y_spans = int(sqrt((double)num_instance_filenames));
-    int num_x_spans = (num_instance_filenames + num_y_spans - 1) / num_y_spans;
-    
-    int inst_width = win_width / num_x_spans;
-    int inst_height = win_height / num_y_spans;
-
-    for (int yi = 0; yi < num_y_spans; ++yi) {
-      for (int xi = 0; xi < num_x_spans; ++xi) {
-        int i = yi * num_x_spans + xi;
-        if (i >= num_instance_filenames) {
-          continue;
+      
+      // Subdivide the window into num_x_spans * num_y_spans sub-windows.
+      int num_y_spans = int(sqrt((double)num_instance_filenames));
+      int num_x_spans = (num_instance_filenames + num_y_spans - 1) / num_y_spans;
+      
+      int inst_width = _win_width / num_x_spans;
+      int inst_height = _win_height / num_y_spans;
+      
+      for (int yi = 0; yi < num_y_spans; ++yi) {
+        for (int xi = 0; xi < num_x_spans; ++xi) {
+          int i = yi * num_x_spans + xi;
+          if (i >= num_instance_filenames) {
+            continue;
+          }
+          
+          // Create instance i at window slot (xi, yi).
+          int inst_x = _win_x + xi * inst_width;
+          int inst_y = _win_y + yi * inst_height;
+          
+          P3D_instance *inst = create_instance
+            (instance_filenames[i], true,
+             inst_x, inst_y, inst_width, inst_height,
+             instance_args, num_instance_args);
+          _instances.insert(inst);
         }
-
-        // Create instance i at window slot (xi, yi).
-        int inst_x = win_x + xi * inst_width;
-        int inst_y = win_y + yi * inst_height;
-
+      }
+      
+    } else {
+      // Not an embedded window.  Create each window with the same parameters.
+      for (int i = 0; i < num_instance_filenames; ++i) {
         P3D_instance *inst = create_instance
-          (instance_filenames[i], P3D_WT_embedded, 
-           inst_x, inst_y, inst_width, inst_height, parent_window,
+          (instance_filenames[i], true, 
+           _win_x, _win_y, _win_width, _win_height,
            instance_args, num_instance_args);
         _instances.insert(inst);
       }
     }
-
-  } else {
-    // Not an embedded window.  Create each window with the same parameters.
-    for (int i = 0; i < num_instance_filenames; ++i) {
-      P3D_instance *inst = create_instance
-        (instance_filenames[i], window_type, 
-         win_x, win_y, win_width, win_height, parent_window,
-         instance_args, num_instance_args);
-      _instances.insert(inst);
-    }
   }
+    
+  run_main_loop();
 
+  // All instances have finished; we can exit.
+  unload_plugin();
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::run_main_loop
+//       Access: Public
+//  Description: Gets lost in the application main loop, waiting for
+//               system events and notifications from the open
+//               instance(s).
+////////////////////////////////////////////////////////////////////
+void Panda3D::
+run_main_loop() {
 #ifdef _WIN32
   if (window_type == P3D_WT_embedded) {
     // Wait for new messages from Windows, and new requests from the
@@ -321,10 +311,10 @@ run(int argc, char *argv[]) {
     MSG msg;
     int retval;
     retval = GetMessage(&msg, NULL, 0, 0);
-    while (retval != 0 && !_instances.empty()) {
+    while (retval != 0 && !time_to_exit()) {
       if (retval == -1) {
         cerr << "Error processing message queue.\n";
-        exit(1);
+        return;
       }
       TranslateMessage(&msg);
       DispatchMessage(&msg);
@@ -357,7 +347,7 @@ run(int argc, char *argv[]) {
   } else {
     // Not an embedded window, so we don't have our own window to
     // generate Windows events.  Instead, just wait for requests.
-    while (!_instances.empty()) {
+    while (!time_to_exit()) {
       P3D_instance *inst = P3D_check_request(wait_cycle);
       if (inst != (P3D_instance *)NULL) {
         P3D_request *request = P3D_instance_get_request(inst);
@@ -391,7 +381,7 @@ run(int argc, char *argv[]) {
 #else  // _WIN32, __APPLE__
 
   // Now wait while we process pending requests.
-  while (!_instances.empty()) {
+  while (!time_to_exit()) {
     P3D_instance *inst = P3D_check_request(wait_cycle);
     if (inst != (P3D_instance *)NULL) {
       P3D_request *request = P3D_instance_get_request(inst);
@@ -403,15 +393,61 @@ run(int argc, char *argv[]) {
   }
 
 #endif  // _WIN32, __APPLE__
+}
 
-  // All instances have finished; we can exit.
-  unload_plugin();
-  return 0;
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::post_arg_processing
+//       Access: Protected
+//  Description: Sets up some internal state after processing the
+//               command-line arguments.  Returns true on success,
+//               false on failure.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+post_arg_processing() {
+  // Set host_url_prefix to end with a slash.
+  _host_url_prefix = _host_url;
+  if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.length() - 1] != '/') {
+    _host_url_prefix += '/';
+  }
+  _download_url_prefix = _host_url_prefix;
+
+  // If the "super mirror" URL is a filename, convert it to a file:// url.
+  if (!_super_mirror_url.empty()) {
+    if (!is_url(_super_mirror_url)) {
+      Filename filename = Filename::from_os_specific(_super_mirror_url);
+      filename.make_absolute();
+      string path = filename.to_os_generic();
+      if (!path.empty() && path[0] != '/') {
+        // On Windows, a leading drive letter must be preceded by an
+        // additional slash.
+        path = "/" + path;
+      }
+      _super_mirror_url = "file://" + path;
+    }
+
+    // And make sure the super_mirror_url_prefix ends with a slash.
+    _super_mirror_url_prefix = _super_mirror_url;
+    if (!_super_mirror_url_prefix.empty() && _super_mirror_url_prefix[_super_mirror_url_prefix.length() - 1] != '/') {
+      _super_mirror_url_prefix += '/';
+    }
+  }
+
+  if (!get_plugin()) {
+    cerr << "Unable to load Panda3D plugin.\n";
+    return false;
+  }
+
+  // Set up the "super mirror" URL, if specified.
+  if (!_super_mirror_url.empty()) {
+    P3D_set_super_mirror(_super_mirror_url.c_str());
+  }
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::get_plugin
-//       Access: Private
+//       Access: Protected
 //  Description: Downloads the contents.xml file from the named URL
 //               and attempts to use it to load the core API.  Returns
 //               true on success, false on failure.
@@ -497,7 +533,7 @@ get_plugin() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::read_contents_file
-//       Access: Private
+//       Access: Protected
 //  Description: Attempts to open and read the contents.xml file on
 //               disk, and uses that data to load the plugin, if
 //               possible.  Returns true on success, false on failure.
@@ -539,7 +575,7 @@ read_contents_file(const Filename &contents_filename) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::find_host
-//       Access: Private
+//       Access: Protected
 //  Description: Scans the <contents> element for the matching <host>
 //               element.
 ////////////////////////////////////////////////////////////////////
@@ -575,7 +611,7 @@ find_host(TiXmlElement *xcontents) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::read_xhost
-//       Access: Private
+//       Access: Protected
 //  Description: Reads the host data from the <host> (or <alt_host>)
 //               entry in the contents.xml file.
 ////////////////////////////////////////////////////////////////////
@@ -611,7 +647,7 @@ read_xhost(TiXmlElement *xhost) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::add_mirror
-//       Access: Private
+//       Access: Protected
 //  Description: Adds a new URL to serve as a mirror for this host.
 //               The mirrors will be consulted first, before
 //               consulting the host directly.
@@ -658,7 +694,7 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::get_core_api
-//       Access: Private
+//       Access: Protected
 //  Description: Checks the core API DLL file against the
 //               specification in the contents file, and downloads it
 //               if necessary.
@@ -756,15 +792,9 @@ get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
 
   bool trusted_environment = !_enable_security;
 
-#ifdef NON_CONSOLE
-  static const bool console_environment = false;
-#else
-  static const bool console_environment = true;
-#endif
-
   if (!load_plugin(pathname, contents_filename.to_os_specific(),
                    _host_url, _verify_contents, _this_platform, _log_dirname,
-                   _log_basename, trusted_environment, console_environment,
+                   _log_basename, trusted_environment, _console_environment,
                    cerr)) {
     cerr << "Unable to launch core API in " << pathname << "\n" << flush;
     return false;
@@ -786,7 +816,7 @@ get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::run_getters
-//       Access: Private
+//       Access: Protected
 //  Description: Polls all of the active URL requests.
 ////////////////////////////////////////////////////////////////////
 void Panda3D::
@@ -810,7 +840,7 @@ run_getters() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::handle_request
-//       Access: Private
+//       Access: Protected
 //  Description: Handles a single request received via the plugin API
 //               from a p3d instance.
 ////////////////////////////////////////////////////////////////////
@@ -871,13 +901,12 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::make_parent_window
-//       Access: Private
+//       Access: Protected
 //  Description: Creates a toplevel window to contain the embedded
 //               instances.  Windows implementation.
 ////////////////////////////////////////////////////////////////////
 void Panda3D::
-make_parent_window(P3D_window_handle &parent_window, 
-                   int win_width, int win_height) {
+make_parent_window() {
   WNDCLASS wc;
 
   HINSTANCE application = GetModuleHandle(NULL);
@@ -899,7 +928,7 @@ make_parent_window(P3D_window_handle &parent_window,
 
   HWND toplevel_window = 
     CreateWindow("panda3d", "Panda3D", window_style,
-                 CW_USEDEFAULT, CW_USEDEFAULT, win_width, win_height,
+                 CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height,
                  NULL, NULL, application, 0);
   if (!toplevel_window) {
     cerr << "Could not create toplevel window!\n";
@@ -907,21 +936,20 @@ make_parent_window(P3D_window_handle &parent_window,
   }
 
   ShowWindow(toplevel_window, SW_SHOWNORMAL);
-
-  parent_window._hwnd = toplevel_window;
+  
+  _parent_window._hwnd = toplevel_window;
 }
 
 #else
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::make_parent_window
-//       Access: Private
+//       Access: Protected
 //  Description: Creates a toplevel window to contain the embedded
 //               instances.
 ////////////////////////////////////////////////////////////////////
 void Panda3D::
-make_parent_window(P3D_window_handle &parent_window, 
-                   int win_width, int win_height) {
+make_parent_window() {
   // TODO.
   assert(false);
 }
@@ -930,20 +958,22 @@ make_parent_window(P3D_window_handle &parent_window,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::create_instance
-//       Access: Private
+//       Access: Protected
 //  Description: Uses the plugin API to create a new P3D instance to
-//               play a particular .p3d file.
+//               play a particular .p3d file.  This instance is also
+//               started if start_instance is true (which requires
+//               that the named p3d file exists).
 ////////////////////////////////////////////////////////////////////
 P3D_instance *Panda3D::
-create_instance(const string &p3d, P3D_window_type window_type,
+create_instance(const string &p3d, bool start_instance,
                 int win_x, int win_y, int win_width, int win_height,
-                P3D_window_handle parent_window, char **args, int num_args) {
+                char **args, int num_args) {
   // Check to see if the p3d filename we were given is a URL, or a
   // local file.
   Filename p3d_filename = Filename::from_os_specific(p3d);
   string os_p3d_filename = p3d;
   bool is_local = !is_url(p3d);
-  if (is_local) {
+  if (is_local && start_instance) {
     if (!p3d_filename.exists()) {
       cerr << "No such file: " << p3d_filename << "\n";
       exit(1);
@@ -967,11 +997,11 @@ create_instance(const string &p3d, P3D_window_type window_type,
   } else {
     // Send output to the console.
     token._keyword = "console_output";
-#ifdef NON_CONSOLE
-    token._value = "0";
-#else
-    token._value = "1";
-#endif
+    if (_console_environment) {
+      token._value = "1";
+    } else {
+      token._value = "0";
+    }
     tokens.push_back(token);
   }
 
@@ -996,11 +1026,15 @@ create_instance(const string &p3d, P3D_window_type window_type,
                                         argv.size(), &argv[0], NULL);
 
   if (inst != NULL) {
-    // We call start() first, to give the core API a chance to notice
-    // the "hidden" attrib before we set the window parameters.
-    P3D_instance_start(inst, is_local, os_p3d_filename.c_str());
+    if (start_instance) {
+      // We call start() first, to give the core API a chance to
+      // notice the "hidden" attrib before we set the window
+      // parameters.
+      P3D_instance_start(inst, is_local, os_p3d_filename.c_str());
+    }
+
     P3D_instance_setup_window
-      (inst, window_type, win_x, win_y, win_width, win_height, parent_window);
+      (inst, _window_type, _win_x, _win_y, _win_width, _win_height, _parent_window);
   }
 
   return inst;
@@ -1008,7 +1042,7 @@ create_instance(const string &p3d, P3D_window_type window_type,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::delete_instance
-//       Access: Private
+//       Access: Protected
 //  Description: Deletes the indicated instance and removes it from
 //               the internal structures.
 ////////////////////////////////////////////////////////////////////
@@ -1034,7 +1068,7 @@ delete_instance(P3D_instance *inst) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::usage
-//       Access: Private
+//       Access: Protected
 //  Description: Reports the available command-line options.
 ////////////////////////////////////////////////////////////////////
 void Panda3D::
@@ -1127,7 +1161,7 @@ usage() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::parse_token
-//       Access: Private
+//       Access: Protected
 //  Description: Parses a web token of the form token=value, and
 //               stores it in _tokens.  Returns true on success, false
 //               on failure.
@@ -1154,7 +1188,7 @@ parse_token(char *arg) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::parse_int_pair
-//       Access: Private
+//       Access: Protected
 //  Description: Parses a string into an x,y pair of integers.
 //               Returns true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
@@ -1175,7 +1209,7 @@ parse_int_pair(char *arg, int &x, int &y) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::is_url
-//       Access: Private, Static
+//       Access: Protected, Static
 //  Description: Returns true if the indicated string appears to be a
 //               URL, with a leading http:// or file:// or whatever,
 //               or false if it must be a local filename instead.
@@ -1208,7 +1242,7 @@ is_url(const string &param) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::report_downloading_package
-//       Access: Private
+//       Access: Protected
 //  Description: Tells the user we have to download a package.
 ////////////////////////////////////////////////////////////////////
 void Panda3D::
@@ -1234,7 +1268,7 @@ report_downloading_package(P3D_instance *instance) {
  
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::report_download_complete
-//       Access: Private
+//       Access: Protected
 //  Description: Tells the user we're done downloading packages
 ////////////////////////////////////////////////////////////////////
 void Panda3D::
@@ -1247,7 +1281,7 @@ report_download_complete(P3D_instance *instance) {
 #ifdef __APPLE__
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::st_timer_callback
-//       Access: Private, Static
+//       Access: Protected, Static
 //  Description: Installed as a timer on the event loop, so we can
 //               process local events, in the Apple implementation.
 ////////////////////////////////////////////////////////////////////
@@ -1260,7 +1294,7 @@ st_timer_callback(EventLoopTimerRef timer, void *user_data) {
 #ifdef __APPLE__
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::timer_callback
-//       Access: Private
+//       Access: Protected
 //  Description: Installed as a timer on the event loop, so we can
 //               process local events, in the Apple implementation.
 ////////////////////////////////////////////////////////////////////
@@ -1280,7 +1314,7 @@ timer_callback(EventLoopTimerRef timer) {
   run_getters();
 
   // If we're out of instances, exit the application.
-  if (_instances.empty()) {
+  if (time_to_exit()) {
     QuitApplicationEventLoop();
   }
 }
@@ -1365,79 +1399,3 @@ run() {
      _bytes_sent, NULL, 0);
   return false;
 }
-
-
-#if defined(_WIN32) && defined(NON_CONSOLE)
-// On Windows, we may need to build panda3dw.exe, a non-console
-// version of this program.
-
-// Returns a newly-allocated string representing the quoted argument
-// beginning at p.  Advances p to the first character following the
-// close quote.
-static char *
-parse_quoted_arg(char *&p) {
-  char quote = *p;
-  ++p;
-  string result;
-
-  while (*p != '\0' && *p != quote) {
-    // TODO: handle escape characters?  Not sure if we need to.
-    result += *p;
-    ++p;
-  }
-  if (*p == quote) {
-    ++p;
-  }
-  return strdup(result.c_str());
-}
-
-// Returns a newly-allocated string representing the unquoted argument
-// beginning at p.  Advances p to the first whitespace following the
-// argument.
-static char *
-parse_unquoted_arg(char *&p) {
-  string result;
-  while (*p != '\0' && !isspace(*p)) {
-    result += *p;
-    ++p;
-  }
-  return strdup(result.c_str());
-}
-
-int WINAPI 
-WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
-  char *command_line = GetCommandLine();
-
-  vector<char *> argv;
-  
-  char *p = command_line;
-  while (*p != '\0') {
-    if (*p == '"') {
-      char *arg = parse_quoted_arg(p);
-      argv.push_back(arg);
-    } else {
-      char *arg = parse_unquoted_arg(p);
-      argv.push_back(arg);
-    }
-
-    // Skip whitespace.
-    while (*p != '\0' && isspace(*p)) {
-      ++p;
-    }
-  }
-
-  assert(!argv.empty());
-
-  Panda3D program;
-  return program.run(argv.size(), &argv[0]);
-}
-
-#else  // NON_CONSOLE
-
-// The normal, "console" program.
-int
-main(int argc, char *argv[]) {
-  Panda3D program;
-  return program.run(argc, argv);
-}
-#endif  // NON_CONSOLE

+ 18 - 9
direct/src/plugin_standalone/panda3d.h

@@ -38,11 +38,13 @@
 ////////////////////////////////////////////////////////////////////
 class Panda3D {
 public:
-  Panda3D();
+  Panda3D(bool console_environment);
 
-  int run(int argc, char *argv[]);
+  int run_command_line(int argc, char *argv[]);
+  void run_main_loop();
 
-private:
+protected:
+  bool post_arg_processing();
   bool get_plugin();
   bool read_contents_file(const Filename &contents_filename);
   void find_host(TiXmlElement *xcontents);
@@ -52,13 +54,11 @@ private:
   bool get_core_api(const Filename &contents_filename, TiXmlElement *xplugin);
   void run_getters();
   void handle_request(P3D_request *request);
-  void make_parent_window(P3D_window_handle &parent_window, 
-                          int win_width, int win_height);
+  void make_parent_window();
 
   P3D_instance *
-  create_instance(const string &p3d, P3D_window_type window_type,
+  create_instance(const string &p3d, bool start_instance,
                   int win_x, int win_y, int win_width, int win_height,
-                  P3D_window_handle parent_window,
                   char **args, int num_args);
   void delete_instance(P3D_instance *instance);
 
@@ -70,28 +70,37 @@ private:
   void report_downloading_package(P3D_instance *instance);
   void report_download_complete(P3D_instance *instance);
 
+  inline bool time_to_exit();
+
 #ifdef __APPLE__
   static pascal void st_timer_callback(EventLoopTimerRef timer, void *user_data);
   void timer_callback(EventLoopTimerRef timer);
 #endif
 
-private:
+protected:
   string _root_dir;
   string _log_dirname;
   string _log_basename;
   string _this_platform;
   bool _verify_contents;
+  P3D_window_type _window_type;
+  P3D_window_handle _parent_window;
+  int _win_x, _win_y;
+  int _win_width, _win_height;
+  bool _exit_with_last_instance;
 
   string _host_url;
-  string _super_mirror_url_prefix;
+  string _super_mirror_url;
   string _host_url_prefix;
   string _download_url_prefix;
+  string _super_mirror_url_prefix;
   typedef pvector<string> Mirrors;
   Mirrors _mirrors;
   
   FileSpec _core_api_dll;
   bool _reporting_download;
   bool _enable_security;
+  bool _console_environment;
 
   typedef pset<P3D_instance *> Instances;
   Instances _instances;

+ 14 - 0
direct/src/plugin_standalone/panda3dMac.I

@@ -0,0 +1,14 @@
+// Filename: panda3dMac.I
+// Created by:  drose (23Oct09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 153 - 0
direct/src/plugin_standalone/panda3dMac.cxx

@@ -0,0 +1,153 @@
+// Filename: panda3dMac.cxx
+// Created by:  drose (23Oct09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "panda3dMac.h"
+#include "load_plugin.h"
+
+#include <iostream>
+#include <fstream>
+using namespace std;
+
+// Having a global Panda3DMac object just makes things easier.
+static Panda3DMac *this_prog;
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DMac::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Panda3DMac::
+Panda3DMac() : Panda3D(false) {
+  // Mac applications traditionally keep running even when all windows
+  // are closed.
+  _exit_with_last_instance = false;
+
+  // No command-line arguments, so just run.
+  if (!post_arg_processing()) {
+    exit(1);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DMac::open_p3d_file
+//       Access: Public
+//  Description: Opens a p3d file received via the "open documents"
+//               event as its own instance.
+////////////////////////////////////////////////////////////////////
+void Panda3DMac::
+open_p3d_file(FSRef *ref) {
+  OSErr err;
+
+  // Get the size and basename of the file.
+  FSCatalogInfo catalog_info;
+  HFSUniStr255 basename_unicode;
+
+  err = FSGetCatalogInfo(ref, kFSCatInfoDataSizes, &catalog_info,
+                         &basename_unicode, NULL, NULL);
+  if (err) {
+    cerr << "Couldn't query file information.\n";
+    return;
+  }
+
+  // A poor-man's unicode-to-ascii conversion.
+  string basename;
+  for (int i = 0; i < basename_unicode.length; ++i) {
+    basename += (char)basename_unicode.unicode[i];
+  }
+  size_t data_size = (size_t)catalog_info.dataLogicalSize;
+
+  // We could try to figure out full pathname of the p3d file we've
+  // got here, but it's probably better just to open the file and read
+  // it.  This way, it works regardless of the source of the p3d file,
+  // even if it's not actually a file on disk.
+  FSIORefNum io_ref;
+  err = FSOpenFork(ref, 0, NULL, fsRdPerm, &io_ref);
+  if (!err) {
+    // Create an instance, and tell it we'll be sending it the p3d
+    // data in a forthcoming stream.
+    P3D_instance *inst = create_instance
+      (basename.c_str(), false, 
+       _win_x, _win_y, _win_width, _win_height,
+       NULL, 0);
+    int stream_id = P3D_instance_start_stream(inst, basename.c_str());
+
+    // Now start to read the data.
+    static const size_t buffer_size = 8192;
+    static char buffer[buffer_size];
+    ByteCount read_count;
+    err = FSReadFork(io_ref, fsAtMark, 0, buffer_size, buffer, &read_count);
+    while (read_count != 0) {
+      P3D_instance_feed_url_stream(inst, stream_id, P3D_RC_in_progress, 0,
+                                   data_size, buffer, read_count);
+      err = FSReadFork(io_ref, fsAtMark, 0, buffer_size, buffer, &read_count);
+    }
+
+    P3D_result_code status = P3D_RC_done;
+    if (err != eofErr) {
+      status = P3D_RC_generic_error;
+      cerr << "Error reading file\n";
+    }
+    
+    P3D_instance_feed_url_stream
+      (inst, stream_id, status, 0, data_size, NULL, 0);
+  }
+}
+
+static pascal OSErr
+open_documents_handler(const AppleEvent *theAppleEvent, AppleEvent *reply, 
+                       long handlerRefcon) {
+  AEDescList docList;
+  FSRef theFSRef;
+  long index;
+  long count = 0;
+  
+  // Get the list of file aliases from the event.
+  OSErr err = AEGetParamDesc(theAppleEvent,
+                             keyDirectObject, typeAEList, &docList);
+  require_noerr(err, CantGetDocList);
+  
+  err = AECountItems(&docList, &count);
+  require_noerr(err, CantGetCount);
+  
+  for (index = 1; index <= count; index++) {
+    err = AEGetNthPtr(&docList, index, typeFSRef,
+                      NULL, NULL, &theFSRef, sizeof(FSRef), NULL);// 5
+    require_noerr(err, CantGetDocDescPtr);
+    
+    // Here's the file, do something with it.
+    this_prog->open_p3d_file(&theFSRef);
+  }
+  
+  // Release list of files
+  AEDisposeDesc(&docList);
+  
+  // Error handlers.
+ CantGetDocList:
+ CantGetCount:
+ CantGetDocDescPtr:
+  return (err);
+}
+
+int
+main(int argc, char *argv[]) {
+  OSErr err;
+  AEEventHandlerUPP handler;
+
+  this_prog = new Panda3DMac;
+  handler = NewAEEventHandlerUPP(open_documents_handler);
+  err = AEInstallEventHandler
+    (kCoreEventClass, kAEOpenDocuments, handler, NULL, false);
+
+  return this_prog->run_command_line(argc, argv);
+}

+ 36 - 0
direct/src/plugin_standalone/panda3dMac.h

@@ -0,0 +1,36 @@
+// Filename: panda3dMac.h
+// Created by:  drose (23Oct09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PANDA3DMAC_H
+#define PANDA3DMAC_H
+
+#include "panda3d.h"
+
+#include <Carbon/Carbon.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : Panda3DMac
+// Description : A specialization of Panda3D for running as a Carbon
+//               application on OS X.  Instead of taking input from
+//               the command line, this program waits quietly for an
+//               "open documents" Apple event.
+////////////////////////////////////////////////////////////////////
+class Panda3DMac : public Panda3D {
+public:
+  Panda3DMac();
+
+  void open_p3d_file(FSRef *ref);
+};
+
+#endif

+ 22 - 0
direct/src/plugin_standalone/panda3dMain.cxx

@@ -0,0 +1,22 @@
+// Filename: panda3dMain.cxx
+// Created by:  drose (23Oct09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "panda3d.h"
+
+// The normal, "console" program.
+int
+main(int argc, char *argv[]) {
+  Panda3D program(true);
+  return program.run_command_line(argc, argv);
+}

+ 80 - 0
direct/src/plugin_standalone/panda3dWinMain.cxx

@@ -0,0 +1,80 @@
+// Filename: panda3dWinMain.cxx
+// Created by:  drose (23Oct09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+#include "panda3d.h"
+
+// On Windows, we may need to build panda3dw.exe, a non-console
+// version of this program.
+
+// Returns a newly-allocated string representing the quoted argument
+// beginning at p.  Advances p to the first character following the
+// close quote.
+static char *
+parse_quoted_arg(char *&p) {
+  char quote = *p;
+  ++p;
+  string result;
+
+  while (*p != '\0' && *p != quote) {
+    // TODO: handle escape characters?  Not sure if we need to.
+    result += *p;
+    ++p;
+  }
+  if (*p == quote) {
+    ++p;
+  }
+  return strdup(result.c_str());
+}
+
+// Returns a newly-allocated string representing the unquoted argument
+// beginning at p.  Advances p to the first whitespace following the
+// argument.
+static char *
+parse_unquoted_arg(char *&p) {
+  string result;
+  while (*p != '\0' && !isspace(*p)) {
+    result += *p;
+    ++p;
+  }
+  return strdup(result.c_str());
+}
+
+int WINAPI 
+WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
+  char *command_line = GetCommandLine();
+
+  vector<char *> argv;
+  
+  char *p = command_line;
+  while (*p != '\0') {
+    if (*p == '"') {
+      char *arg = parse_quoted_arg(p);
+      argv.push_back(arg);
+    } else {
+      char *arg = parse_unquoted_arg(p);
+      argv.push_back(arg);
+    }
+
+    // Skip whitespace.
+    while (*p != '\0' && isspace(*p)) {
+      ++p;
+    }
+  }
+
+  assert(!argv.empty());
+
+  Panda3D program(false);
+  return program.run_command_line(argv.size(), &argv[0]);
+}

+ 55 - 0
direct/src/plugin_standalone/panda3d_mac.plist

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleDisplayName</key>
+	<string>Panda3D Runtime</string>
+	<key>CFBundleDocumentTypes</key>
+	<array>
+		<dict>
+			<key>CFBundleTypeName</key>
+			<string>Panda3D applet</string>
+                        <key>CFBundleTypeIconFile</key>
+                        <string>panda3d.icns</string>
+			<key>CFBundleTypeExtensions</key>
+			<array>
+				<string>p3d</string>
+			</array>
+			<key>CFBundleTypeMIMETypes</key>
+			<array>
+                                <string>application/x-panda3d</string>
+			</array>
+			<key>CFBundleTypeRole</key>
+			<string>Viewer</string>
+                        <key>LSTypeIsPackage</key>
+                        <false/>
+		</dict>
+	</array>
+	<key>CFBundleExecutable</key>
+	<string>panda3d_mac</string>
+	<key>CFBundleIconFile</key>
+	<string>panda3d.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.panda3d.runtime</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>Panda3D</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>0.9.3</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>0.9.3</string>
+	<key>LSHasLocalizedDisplayName</key>
+	<false/>
+	<key>NSAppleScriptEnabled</key>
+	<false/>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

BIN
models/plugin_images/download.png


BIN
models/plugin_images/failed.png


BIN
models/plugin_images/failed.xcf


BIN
models/plugin_images/panda3d.icns