diff --git a/direct/src/plugin_npapi/make_osx_bundle.py b/direct/src/plugin_npapi/make_osx_bundle.py index 3a6be84f3c..0c6d4af006 100755 --- a/direct/src/plugin_npapi/make_osx_bundle.py +++ b/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() diff --git a/direct/src/plugin_standalone/Sources.pp b/direct/src/plugin_standalone/Sources.pp index 6453d90b99..b2c215aeb2 100644 --- a/direct/src/plugin_standalone/Sources.pp +++ b/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 diff --git a/direct/src/plugin_standalone/panda3d.I b/direct/src/plugin_standalone/panda3d.I index ec049a6324..8e668d8bc0 100755 --- a/direct/src/plugin_standalone/panda3d.I +++ b/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; } - diff --git a/direct/src/plugin_standalone/panda3d.cxx b/direct/src/plugin_standalone/panda3d.cxx index e705a1997b..28a9995840 100644 --- a/direct/src/plugin_standalone/panda3d.cxx +++ b/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 element for the matching // element. //////////////////////////////////////////////////////////////////// @@ -575,7 +611,7 @@ find_host(TiXmlElement *xcontents) { //////////////////////////////////////////////////////////////////// // Function: Panda3D::read_xhost -// Access: Private +// Access: Protected // Description: Reads the host data from the (or ) // 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 ¶m) { //////////////////////////////////////////////////////////////////// // 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 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 diff --git a/direct/src/plugin_standalone/panda3d.h b/direct/src/plugin_standalone/panda3d.h index 7e44fcdd31..64a0494c48 100755 --- a/direct/src/plugin_standalone/panda3d.h +++ b/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 Mirrors; Mirrors _mirrors; FileSpec _core_api_dll; bool _reporting_download; bool _enable_security; + bool _console_environment; typedef pset Instances; Instances _instances; diff --git a/direct/src/plugin_standalone/panda3dMac.I b/direct/src/plugin_standalone/panda3dMac.I new file mode 100644 index 0000000000..7b25d4fd7d --- /dev/null +++ b/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." +// +//////////////////////////////////////////////////////////////////// + diff --git a/direct/src/plugin_standalone/panda3dMac.cxx b/direct/src/plugin_standalone/panda3dMac.cxx new file mode 100644 index 0000000000..d52da4f2f6 --- /dev/null +++ b/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 +#include +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); +} diff --git a/direct/src/plugin_standalone/panda3dMac.h b/direct/src/plugin_standalone/panda3dMac.h new file mode 100644 index 0000000000..a5291c7642 --- /dev/null +++ b/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 + +//////////////////////////////////////////////////////////////////// +// 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 diff --git a/direct/src/plugin_standalone/panda3dMain.cxx b/direct/src/plugin_standalone/panda3dMain.cxx new file mode 100644 index 0000000000..e0ed95e660 --- /dev/null +++ b/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); +} diff --git a/direct/src/plugin_standalone/panda3dWinMain.cxx b/direct/src/plugin_standalone/panda3dWinMain.cxx new file mode 100644 index 0000000000..6007ee2dbb --- /dev/null +++ b/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 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]); +} diff --git a/direct/src/plugin_standalone/panda3d_mac.plist b/direct/src/plugin_standalone/panda3d_mac.plist new file mode 100644 index 0000000000..ab32a790ee --- /dev/null +++ b/direct/src/plugin_standalone/panda3d_mac.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + Panda3D Runtime + CFBundleDocumentTypes + + + CFBundleTypeName + Panda3D applet + CFBundleTypeIconFile + panda3d.icns + CFBundleTypeExtensions + + p3d + + CFBundleTypeMIMETypes + + application/x-panda3d + + CFBundleTypeRole + Viewer + LSTypeIsPackage + + + + CFBundleExecutable + panda3d_mac + CFBundleIconFile + panda3d.icns + CFBundleIdentifier + org.panda3d.runtime + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Panda3D + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.9.3 + CFBundleSignature + ???? + CFBundleVersion + 0.9.3 + LSHasLocalizedDisplayName + + NSAppleScriptEnabled + + NSPrincipalClass + NSApplication + + diff --git a/models/plugin_images/download.png b/models/plugin_images/download.png index 1bc40911f5..6a1f9e5784 100644 Binary files a/models/plugin_images/download.png and b/models/plugin_images/download.png differ diff --git a/models/plugin_images/failed.png b/models/plugin_images/failed.png index f7831995b5..fd551d1b83 100644 Binary files a/models/plugin_images/failed.png and b/models/plugin_images/failed.png differ diff --git a/models/plugin_images/failed.xcf b/models/plugin_images/failed.xcf index 2688a4f3db..3f8e8ebbfe 100644 Binary files a/models/plugin_images/failed.xcf and b/models/plugin_images/failed.xcf differ diff --git a/models/plugin_images/panda3d.icns b/models/plugin_images/panda3d.icns new file mode 100644 index 0000000000..08c699f0fc Binary files /dev/null and b/models/plugin_images/panda3d.icns differ