diff --git a/direct/src/plugin/p3dFileParams.I b/direct/src/plugin/p3dFileParams.I index 9ef3954c20..1b1c0efb0c 100644 --- a/direct/src/plugin/p3dFileParams.I +++ b/direct/src/plugin/p3dFileParams.I @@ -23,3 +23,14 @@ inline const string &P3DFileParams:: get_p3d_filename() const { return _p3d_filename; } + +//////////////////////////////////////////////////////////////////// +// Function: P3DFileParams::get_p3d_offset +// Access: Public +// Description: Returns the p3d file offset, the location +// in the file where the p3d data starts. +//////////////////////////////////////////////////////////////////// +inline const int &P3DFileParams:: +get_p3d_offset() const { + return _p3d_offset; +} diff --git a/direct/src/plugin/p3dFileParams.cxx b/direct/src/plugin/p3dFileParams.cxx index a0773e06e8..decb00b280 100644 --- a/direct/src/plugin/p3dFileParams.cxx +++ b/direct/src/plugin/p3dFileParams.cxx @@ -21,7 +21,7 @@ // Description: //////////////////////////////////////////////////////////////////// P3DFileParams:: -P3DFileParams() { +P3DFileParams() : _p3d_offset(0) { } //////////////////////////////////////////////////////////////////// @@ -32,6 +32,7 @@ P3DFileParams() { P3DFileParams:: P3DFileParams(const P3DFileParams ©) : _p3d_filename(copy._p3d_filename), + _p3d_offset(copy._p3d_offset), _tokens(copy._tokens), _args(copy._args) { @@ -45,6 +46,7 @@ P3DFileParams(const P3DFileParams ©) : void P3DFileParams:: operator = (const P3DFileParams &other) { _p3d_filename = other._p3d_filename; + _p3d_offset = other._p3d_offset; _tokens = other._tokens; _args = other._args; } @@ -59,6 +61,17 @@ set_p3d_filename(const string &p3d_filename) { _p3d_filename = p3d_filename; } +//////////////////////////////////////////////////////////////////// +// Function: P3DFileParams::set_p3d_filename +// Access: Public +// Description: Specifies the location in the file where +// the p3d file data starts. +//////////////////////////////////////////////////////////////////// +void P3DFileParams:: +set_p3d_offset(const int &p3d_offset) { + _p3d_offset = p3d_offset; +} + //////////////////////////////////////////////////////////////////// // Function: P3DFileParams::set_tokens // Access: Public @@ -165,6 +178,7 @@ make_xml() { TiXmlElement *xfparams = new TiXmlElement("fparams"); xfparams->SetAttribute("p3d_filename", _p3d_filename); + xfparams->SetAttribute("p3d_offset", _p3d_offset); Tokens::const_iterator ti; for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { diff --git a/direct/src/plugin/p3dFileParams.h b/direct/src/plugin/p3dFileParams.h index 31c1859cba..5aa3ffcf1e 100644 --- a/direct/src/plugin/p3dFileParams.h +++ b/direct/src/plugin/p3dFileParams.h @@ -31,10 +31,12 @@ public: void operator = (const P3DFileParams &other); void set_p3d_filename(const string &p3d_filename); + void set_p3d_offset(const int &p3d_offset); void set_tokens(const P3D_token tokens[], size_t num_tokens); void set_args(int argc, const char *argv[]); inline const string &get_p3d_filename() const; + inline const int &get_p3d_offset() const; string lookup_token(const string &keyword) const; int lookup_token_int(const string &keyword) const; bool has_token(const string &keyword) const; @@ -51,6 +53,7 @@ private: typedef vector Args; string _p3d_filename; + int _p3d_offset; Tokens _tokens; Args _args; }; diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index e9776f372e..b2e615851b 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -211,7 +211,7 @@ P3DInstance:: if (_auth_session != NULL) { _auth_session->shutdown(false); - unref_delete(_auth_session); + p3d_unref_delete(_auth_session); _auth_session = NULL; } @@ -389,9 +389,9 @@ make_p3d_stream(const string &p3d_url) { // Normally this is only called once. //////////////////////////////////////////////////////////////////// void P3DInstance:: -set_p3d_filename(const string &p3d_filename) { +set_p3d_filename(const string &p3d_filename, const int &p3d_offset) { determine_p3d_basename(p3d_filename); - priv_set_p3d_filename(p3d_filename); + priv_set_p3d_filename(p3d_filename, p3d_offset); } //////////////////////////////////////////////////////////////////// @@ -789,7 +789,7 @@ feed_url_stream(int unique_id, if (!download_ok || download->get_download_finished()) { // All done. _downloads.erase(di); - unref_delete(download); + p3d_unref_delete(download); } return download_ok; @@ -1239,7 +1239,7 @@ auth_button_clicked() { // Delete the previous session and create a new one. if (_auth_session != NULL) { _auth_session->shutdown(false); - unref_delete(_auth_session); + p3d_unref_delete(_auth_session); _auth_session = NULL; } @@ -1311,7 +1311,7 @@ auth_finished_main_thread() { // internally, and might be passed a temporary filename. //////////////////////////////////////////////////////////////////// void P3DInstance:: -priv_set_p3d_filename(const string &p3d_filename) { +priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset) { if (!_fparams.get_p3d_filename().empty()) { nout << "p3d_filename already set to: " << _fparams.get_p3d_filename() << ", trying to set to " << p3d_filename << "\n"; @@ -1319,6 +1319,10 @@ priv_set_p3d_filename(const string &p3d_filename) { } _fparams.set_p3d_filename(p3d_filename); + // The default for p3d_offset is -1, which means not to change it. + if (p3d_offset >= 0) { + _fparams.set_p3d_offset(p3d_offset); + } _got_fparams = true; _panda_script_object->set_float_property("instanceDownloadProgress", 1.0); @@ -1328,8 +1332,13 @@ priv_set_p3d_filename(const string &p3d_filename) { // if Python has not yet started). send_notify("onpluginload"); - if (!_mf_reader.open_read(_fparams.get_p3d_filename())) { - nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n"; + if (!_mf_reader.open_read(_fparams.get_p3d_filename(), _fparams.get_p3d_offset())) { + if (_fparams.get_p3d_offset() == 0) { + nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n"; + } else { + nout << "Couldn't read " << _fparams.get_p3d_filename() + << " at offset " << _fparams.get_p3d_offset() << "\n"; + } set_failed(); return; } diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index a0ae90b236..a1eae0da37 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -58,7 +58,7 @@ public: ~P3DInstance(); void set_p3d_url(const string &p3d_url); - void set_p3d_filename(const string &p3d_filename); + void set_p3d_filename(const string &p3d_filename, const int &p3d_offset = 0); int make_p3d_stream(const string &p3d_url); inline const P3DFileParams &get_fparams() const; @@ -156,7 +156,7 @@ private: IT_num_image_types, // Not a real value }; - void priv_set_p3d_filename(const string &p3d_filename); + void priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset = -1); void determine_p3d_basename(const string &p3d_url); bool check_matches_origin(const string &origin_match); diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx index 8dd65ad672..a36de57bae 100644 --- a/direct/src/plugin/p3dInstanceManager.cxx +++ b/direct/src/plugin/p3dInstanceManager.cxx @@ -129,7 +129,7 @@ P3DInstanceManager:: assert(_sessions.empty()); if (_auth_session != NULL) { - unref_delete(_auth_session); + p3d_unref_delete(_auth_session); _auth_session = NULL; } @@ -424,13 +424,13 @@ create_instance(P3D_request_ready_func *func, //////////////////////////////////////////////////////////////////// bool P3DInstanceManager:: set_p3d_filename(P3DInstance *inst, bool is_local, - const string &p3d_filename) { + const string &p3d_filename, const int &p3d_offset) { if (inst->is_started()) { nout << "Instance started twice: " << inst << "\n"; return false; } if (is_local) { - inst->set_p3d_filename(p3d_filename); + inst->set_p3d_filename(p3d_filename, p3d_offset); } else { inst->set_p3d_url(p3d_filename); } @@ -513,11 +513,11 @@ finish_instance(P3DInstance *inst) { if (session->get_num_instances() == 0) { _sessions.erase(session->get_session_key()); session->shutdown(); - unref_delete(session); + p3d_unref_delete(session); } } - unref_delete(inst); + p3d_unref_delete(inst); } //////////////////////////////////////////////////////////////////// @@ -534,7 +534,7 @@ authorize_instance(P3DInstance *inst) { // We only want one auth_session window open at a time, to // minimize user confusion, so close any previous window. _auth_session->shutdown(true); - unref_delete(_auth_session); + p3d_unref_delete(_auth_session); _auth_session = NULL; } diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h index 990a002d50..203a08b92f 100644 --- a/direct/src/plugin/p3dInstanceManager.h +++ b/direct/src/plugin/p3dInstanceManager.h @@ -93,7 +93,7 @@ public: int argc, const char *argv[], void *user_data); bool set_p3d_filename(P3DInstance *inst, bool is_local, - const string &p3d_filename); + const string &p3d_filename, const int &p3d_offset); int make_p3d_stream(P3DInstance *inst, const string &p3d_url); bool start_instance(P3DInstance *inst); void finish_instance(P3DInstance *inst); diff --git a/direct/src/plugin/p3dMultifileReader.cxx b/direct/src/plugin/p3dMultifileReader.cxx index 6c03c40338..0511a71895 100644 --- a/direct/src/plugin/p3dMultifileReader.cxx +++ b/direct/src/plugin/p3dMultifileReader.cxx @@ -39,6 +39,7 @@ const int P3DMultifileReader::_current_minor_ver = 1; P3DMultifileReader:: P3DMultifileReader() { _is_open = false; + _read_offset = 0; } //////////////////////////////////////////////////////////////////// @@ -48,10 +49,12 @@ P3DMultifileReader() { // on success, false on failure. //////////////////////////////////////////////////////////////////// bool P3DMultifileReader:: -open_read(const string &pathname) { +open_read(const string &pathname, const int &offset) { if (_is_open) { close(); } + + _read_offset = offset; if (!read_header(pathname)) { return false; } @@ -220,7 +223,7 @@ read_header(const string &pathname) { } char this_header[_header_size]; - _in.seekg(0); + _in.seekg(_read_offset); // Here's a special case: if the multifile begins with a hash // character, then we continue reading and discarding lines of ASCII @@ -296,7 +299,7 @@ read_index() { } while (next_entry != 0) { Subfile s; - s._index_start = (size_t)_in.tellg(); + s._index_start = (size_t)_in.tellg() - _read_offset; s._index_length = 0; s._data_start = read_uint32(); s._data_length = read_uint32(); @@ -319,7 +322,7 @@ read_index() { s._filename = string(buffer, name_length); delete[] buffer; - s._index_length = (size_t)_in.tellg() - s._index_start; + s._index_length = (size_t)_in.tellg() - s._index_start - _read_offset; if (flags & SF_signature) { // A subfile with this bit set is a signature. @@ -334,7 +337,7 @@ read_index() { } } - _in.seekg(next_entry); + _in.seekg(next_entry + _read_offset); next_entry = read_uint32(); if (!_in) { return false; @@ -353,7 +356,7 @@ read_index() { //////////////////////////////////////////////////////////////////// bool P3DMultifileReader:: extract_subfile(ostream &out, const Subfile &s) { - _in.seekg(s._data_start); + _in.seekg(s._data_start + _read_offset); static const size_t buffer_size = 4096; char buffer[buffer_size]; @@ -397,7 +400,7 @@ check_signatures() { Subfile *subfile = &(*pi); // Extract the signature data and certificate separately. - _in.seekg(subfile->_data_start); + _in.seekg(subfile->_data_start + _read_offset); size_t sig_size = read_uint32(); char *sig = new char[sig_size]; _in.read(sig, sig_size); @@ -410,7 +413,7 @@ check_signatures() { size_t num_certs = read_uint32(); // Read the remaining buffer of certificate data. - size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start; + size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start - _read_offset; size_t buffer_size = subfile->_data_length - bytes_read; char *buffer = new char[buffer_size]; _in.read(buffer, buffer_size); @@ -463,7 +466,7 @@ check_signatures() { // Read and hash the multifile contents, but only up till // _last_data_byte. - _in.seekg(0); + _in.seekg(_read_offset); streampos bytes_remaining = (streampos)_last_data_byte; static const size_t buffer_size = 4096; char buffer[buffer_size]; diff --git a/direct/src/plugin/p3dMultifileReader.h b/direct/src/plugin/p3dMultifileReader.h index a4ba0d3711..fd6af16e2d 100644 --- a/direct/src/plugin/p3dMultifileReader.h +++ b/direct/src/plugin/p3dMultifileReader.h @@ -30,7 +30,7 @@ class P3DMultifileReader { public: P3DMultifileReader(); - bool open_read(const string &pathname); + bool open_read(const string &pathname, const int &offset = 0); inline bool is_open() const; void close(); @@ -82,6 +82,7 @@ private: ifstream _in; bool _is_open; + int _read_offset; typedef vector Subfiles; Subfiles _subfiles; diff --git a/direct/src/plugin/p3dPackage.cxx b/direct/src/plugin/p3dPackage.cxx index 6aad3701e5..bb02bb4de8 100755 --- a/direct/src/plugin/p3dPackage.cxx +++ b/direct/src/plugin/p3dPackage.cxx @@ -429,7 +429,7 @@ contents_file_redownload_finished(bool success) { } else { download->resume_download_finished(false); - unref_delete(download); + p3d_unref_delete(download); } } } @@ -1108,7 +1108,7 @@ void P3DPackage:: set_active_download(Download *download) { if (_active_download != download) { if (_active_download != NULL) { - unref_delete(_active_download); + p3d_unref_delete(_active_download); } _active_download = download; if (_active_download != NULL) { @@ -1127,7 +1127,7 @@ void P3DPackage:: set_saved_download(Download *download) { if (_saved_download != download) { if (_saved_download != NULL) { - unref_delete(_saved_download); + p3d_unref_delete(_saved_download); } _saved_download = download; if (_saved_download != NULL) { @@ -1350,7 +1350,7 @@ InstallStepDownloadFile(P3DPackage *package, const FileSpec &file) : P3DPackage::InstallStepDownloadFile:: ~InstallStepDownloadFile() { if (_download != NULL) { - unref_delete(_download); + p3d_unref_delete(_download); } } @@ -1577,6 +1577,7 @@ P3DPackage::InstallToken P3DPackage::InstallStepUnpackArchive:: do_step(bool download_finished) { string source_pathname = _package->get_archive_file_pathname(); P3DMultifileReader reader; + if (!reader.open_read(source_pathname)) { nout << "Couldn't read " << source_pathname << "\n"; return IT_step_failed; diff --git a/direct/src/plugin/p3dPythonObject.cxx b/direct/src/plugin/p3dPythonObject.cxx index 6063201993..c22307ba5c 100644 --- a/direct/src/plugin/p3dPythonObject.cxx +++ b/direct/src/plugin/p3dPythonObject.cxx @@ -38,7 +38,7 @@ P3DPythonObject:: // child process that we no longer need the corresponding PyObject // to be kept around. _session->drop_pyobj(_object_id); - unref_delete(_session); + p3d_unref_delete(_session); } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index ca60eea257..51aa4c26c5 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -1262,10 +1262,15 @@ add_package_info(P3DCInstance *inst, TiXmlElement *xpackage) { void P3DPythonRun:: set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) { string p3d_filename; + int p3d_offset; const char *p3d_filename_c = xfparams->Attribute("p3d_filename"); if (p3d_filename_c != NULL) { p3d_filename = p3d_filename_c; } + const char *p3d_offset_c = xfparams->Attribute("p3d_offset"); + if (p3d_offset_c != NULL) { + p3d_offset = atoi(p3d_offset_c); + } PyObject *token_list = PyList_New(0); TiXmlElement *xtoken = xfparams->FirstChildElement("token"); @@ -1306,8 +1311,8 @@ set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) { } PyObject *result = PyObject_CallMethod - (_runner, (char *)"setP3DFilename", (char *)"sOOii", p3d_filename.c_str(), - token_list, arg_list, inst->get_instance_id(), _interactive_console); + (_runner, (char *)"setP3DFilename", (char *)"sOOiii", p3d_filename.c_str(), + token_list, arg_list, inst->get_instance_id(), _interactive_console, p3d_offset); Py_DECREF(token_list); Py_DECREF(arg_list); diff --git a/direct/src/plugin/p3dReferenceCount.I b/direct/src/plugin/p3dReferenceCount.I index f0ec37ef41..f9ba1140eb 100644 --- a/direct/src/plugin/p3dReferenceCount.I +++ b/direct/src/plugin/p3dReferenceCount.I @@ -47,7 +47,7 @@ ref() const { // Function: P3DReferenceCount::unref // Access: Public // Description: Explicitly decrements the reference count. Usually, -// you should call unref_delete() instead. +// you should call p3d_unref_delete() instead. // // The return value is true if the new reference count // is nonzero, false if it is zero. @@ -68,14 +68,14 @@ get_ref_count() const { } //////////////////////////////////////////////////////////////////// -// Function: unref_delete +// Function: p3d_unref_delete // Description: This global helper function will unref the given // P3DReferenceCount object, and if the reference count // reaches zero, automatically delete it. //////////////////////////////////////////////////////////////////// template inline void -unref_delete(RefCountType *ptr) { +p3d_unref_delete(RefCountType *ptr) { if (!ptr->unref()) { delete ptr; } diff --git a/direct/src/plugin/p3dReferenceCount.h b/direct/src/plugin/p3dReferenceCount.h index 9cc5965159..71209db1d6 100644 --- a/direct/src/plugin/p3dReferenceCount.h +++ b/direct/src/plugin/p3dReferenceCount.h @@ -38,7 +38,7 @@ private: }; template -inline void unref_delete(RefCountType *ptr); +inline void p3d_unref_delete(RefCountType *ptr); #include "p3dReferenceCount.I" diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index d9c0f27a00..64d0c627e5 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -279,7 +279,7 @@ terminate_instance(P3DInstance *inst) { _instances.erase(inst->get_instance_id()); } RELEASE_LOCK(_instances_lock); - unref_delete(inst); + p3d_unref_delete(inst); } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3d_plugin.cxx b/direct/src/plugin/p3d_plugin.cxx index f33d39d707..0ec23c05c8 100644 --- a/direct/src/plugin/p3d_plugin.cxx +++ b/direct/src/plugin/p3d_plugin.cxx @@ -132,7 +132,7 @@ P3D_new_instance(P3D_request_ready_func *func, bool P3D_instance_start(P3D_instance *instance, bool is_local, - const char *p3d_filename) { + const char *p3d_filename, const int p3d_offset) { assert(P3DInstanceManager::get_global_ptr()->is_initialized()); if (p3d_filename == NULL) { p3d_filename = ""; @@ -145,7 +145,8 @@ P3D_instance_start(P3D_instance *instance, bool is_local, // We don't actually start it immediately; the instance will have // to download the p3d url and read it, reading the python // version, before it can start. - result = inst_mgr->set_p3d_filename(inst, is_local, p3d_filename); + result = inst_mgr->set_p3d_filename(inst, is_local, + p3d_filename, p3d_offset); } RELEASE_LOCK(_api_lock); return result; diff --git a/direct/src/plugin/p3d_plugin.h b/direct/src/plugin/p3d_plugin.h index 0059240da6..1013a6fc3e 100644 --- a/direct/src/plugin/p3d_plugin.h +++ b/direct/src/plugin/p3d_plugin.h @@ -342,7 +342,7 @@ P3D_new_instance_func(P3D_request_ready_func *func, The return value is true on success, false on failure. */ typedef bool P3D_instance_start_func(P3D_instance *instance, bool is_local, - const char *p3d_filename); + const char *p3d_filename, const int p3d_offset = 0); /* This function is an alternative to P3D_instance_start(); it indicates an intention to feed the p3d file data to the instance as diff --git a/direct/src/plugin_standalone/p3dEmbed.cxx b/direct/src/plugin_standalone/p3dEmbed.cxx new file mode 100644 index 0000000000..0852ddd104 --- /dev/null +++ b/direct/src/plugin_standalone/p3dEmbed.cxx @@ -0,0 +1,25 @@ +// Filename: p3dEmbed.cxx +// Created by: rdb (07Dec09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "p3d_plugin_composite1.cxx" +#include "panda3dBase.cxx" +#include + +const uint32_t p3d_offset = 0xFF3D3D00; + +int +main(int argc, char *argv[]) { + Panda3DBase program(true); + return program.run_embedded(p3d_offset, argc, argv); +} diff --git a/direct/src/plugin_standalone/panda3d.I b/direct/src/plugin_standalone/panda3d.I index 8e668d8bc0..54cac83e3b 100755 --- a/direct/src/plugin_standalone/panda3d.I +++ b/direct/src/plugin_standalone/panda3d.I @@ -11,27 +11,3 @@ // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////////////// -// 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 -// Description: Returns the P3D_instance associated with this -// URLGetter. -//////////////////////////////////////////////////////////////////// -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 72f1ea3326..07840c3c03 100644 --- a/direct/src/plugin_standalone/panda3d.cxx +++ b/direct/src/plugin_standalone/panda3d.cxx @@ -13,11 +13,11 @@ //////////////////////////////////////////////////////////////////// #include "panda3d.h" -#include "httpClient.h" #include "load_plugin.h" -#include "find_root_dir.h" #include "p3d_plugin_config.h" +#include "panda3dBase.cxx" + // We can include this header file to get the DTOOL_PLATFORM // definition, even though we don't link with dtool. #include "dtool_platform.h" @@ -40,38 +40,13 @@ #endif #endif - -// The amount of time in seconds to wait for new messages. -static const double wait_cycle = 0.2; - //////////////////////////////////////////////////////////////////// // Function: Panda3D::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// 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)); +Panda3D(bool console_environment) : Panda3DBase(console_environment) { } //////////////////////////////////////////////////////////////////// @@ -298,106 +273,6 @@ run_command_line(int argc, char *argv[]) { 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 - // plugin. - MSG msg; - int retval; - retval = GetMessage(&msg, NULL, 0, 0); - while (retval != 0 && !time_to_exit()) { - if (retval == -1) { - cerr << "Error processing message queue.\n"; - return; - } - TranslateMessage(&msg); - DispatchMessage(&msg); - - // Check for new requests from the Panda3D plugin. - P3D_instance *inst = P3D_check_request(wait_cycle); - while (inst != (P3D_instance *)NULL) { - P3D_request *request = P3D_instance_get_request(inst); - if (request != (P3D_request *)NULL) { - handle_request(request); - } - inst = P3D_check_request(wait_cycle); - } - - while (!_url_getters.empty() && - !PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { - // If there are no Windows messages, check the download tasks. - run_getters(); - } - retval = GetMessage(&msg, NULL, 0, 0); - } - - // WM_QUIT has been received. Terminate all instances, and fall - // through. - while (!_instances.empty()) { - P3D_instance *inst = *(_instances.begin()); - delete_instance(inst); - } - - } else { - // Not an embedded window, so we don't have our own window to - // generate Windows events. Instead, just wait for requests. - 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); - if (request != (P3D_request *)NULL) { - handle_request(request); - } - } - run_getters(); - } - } - -#elif defined(__APPLE__) - // OSX really prefers to own the main loop, so we install a timer to - // call out to our instances and getters, rather than polling within - // the event loop as we do in the Windows case, above. - EventLoopRef main_loop = GetMainEventLoop(); - EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback); - EventLoopTimerRef timer; - EventTimerInterval interval = wait_cycle * kEventDurationSecond; - InstallEventLoopTimer(main_loop, interval, interval, - timer_upp, this, &timer); - RunApplicationEventLoop(); - RemoveEventLoopTimer(timer); - - // Terminate all instances, and fall through. - while (!_instances.empty()) { - P3D_instance *inst = *(_instances.begin()); - delete_instance(inst); - } - -#else // _WIN32, __APPLE__ - - // Now wait while we process pending requests. - 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); - if (request != (P3D_request *)NULL) { - handle_request(request); - } - } - run_getters(); - } - -#endif // _WIN32, __APPLE__ -} - //////////////////////////////////////////////////////////////////// // Function: Panda3D::post_arg_processing // Access: Protected @@ -817,260 +692,6 @@ get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) { return true; } -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::run_getters -// Access: Protected -// Description: Polls all of the active URL requests. -//////////////////////////////////////////////////////////////////// -void Panda3D:: -run_getters() { - URLGetters::iterator gi; - gi = _url_getters.begin(); - while (gi != _url_getters.end()) { - URLGetter *getter = (*gi); - if (getter->run()) { - // This URLGetter is still working. Leave it. - ++gi; - } else { - // This URLGetter is done. Remove it and delete it. - URLGetters::iterator dgi = gi; - ++gi; - _url_getters.erase(dgi); - delete getter; - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::handle_request -// Access: Protected -// Description: Handles a single request received via the plugin API -// from a p3d instance. -//////////////////////////////////////////////////////////////////// -void Panda3D:: -handle_request(P3D_request *request) { - bool handled = false; - switch (request->_request_type) { - case P3D_RT_stop: - delete_instance(request->_instance); -#ifdef _WIN32 - // Post a silly message to spin the event loop. - PostMessage(NULL, WM_USER, 0, 0); -#endif - handled = true; - break; - - case P3D_RT_get_url: - { - int unique_id = request->_request._get_url._unique_id; - const string &url = request->_request._get_url._url; - URLGetter *getter = new URLGetter - (request->_instance, unique_id, URLSpec(url), ""); - _url_getters.insert(getter); - handled = true; - } - break; - - case P3D_RT_notify: - { - if (strcmp(request->_request._notify._message, "ondownloadnext") == 0) { - // Tell the user we're downloading a package. - report_downloading_package(request->_instance); - } else if (strcmp(request->_request._notify._message, "ondownloadcomplete") == 0) { - // Tell the user we're done downloading. - report_download_complete(request->_instance); - } - } - break; - - default: - break; - }; - - P3D_request_finish(request, handled); -} - -#ifdef _WIN32 -LONG WINAPI -window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - switch (msg) { - case WM_DESTROY: - PostQuitMessage(0); - break; - }; - - return DefWindowProc(hwnd, msg, wparam, lparam); -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::make_parent_window -// Access: Protected -// Description: Creates a toplevel window to contain the embedded -// instances. Windows implementation. -//////////////////////////////////////////////////////////////////// -void Panda3D:: -make_parent_window() { - WNDCLASS wc; - - HINSTANCE application = GetModuleHandle(NULL); - ZeroMemory(&wc, sizeof(WNDCLASS)); - wc.lpfnWndProc = window_proc; - wc.hInstance = application; - wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); - wc.lpszClassName = "panda3d"; - - if (!RegisterClass(&wc)) { - cerr << "Could not register window class!\n"; - exit(1); - } - - DWORD window_style = - WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | - WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | - WS_SIZEBOX | WS_MAXIMIZEBOX; - - HWND toplevel_window = - CreateWindow("panda3d", "Panda3D", window_style, - CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height, - NULL, NULL, application, 0); - if (!toplevel_window) { - cerr << "Could not create toplevel window!\n"; - exit(1); - } - - ShowWindow(toplevel_window, SW_SHOWNORMAL); - - memset(&_parent_window, 0, sizeof(_parent_window)); - _parent_window._window_handle_type = P3D_WHT_win_hwnd; - _parent_window._handle._win_hwnd._hwnd = toplevel_window; -} - -#else - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::make_parent_window -// Access: Protected -// Description: Creates a toplevel window to contain the embedded -// instances. -//////////////////////////////////////////////////////////////////// -void Panda3D:: -make_parent_window() { - // TODO. - assert(false); -} - -#endif - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::create_instance -// Access: Protected -// Description: Uses the plugin API to create a new P3D instance to -// 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, bool start_instance, - int win_x, int win_y, int win_width, int win_height, - 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 && start_instance) { - if (!p3d_filename.exists()) { - cerr << "No such file: " << p3d_filename << "\n"; - exit(1); - } - - p3d_filename.make_absolute(); - os_p3d_filename = p3d_filename.to_os_specific(); - } - - // Build up the token list. - Tokens tokens = _tokens; - P3D_token token; - - string log_basename; - if (!_log_dirname.empty()) { - // Generate output to a logfile. - log_basename = p3d_filename.get_basename_wo_extension(); - token._keyword = "log_basename"; - token._value = log_basename.c_str(); - tokens.push_back(token); - } else { - // Send output to the console. - token._keyword = "console_output"; - if (_console_environment) { - token._value = "1"; - } else { - token._value = "0"; - } - tokens.push_back(token); - } - - token._keyword = "auto_start"; - token._value = "1"; - tokens.push_back(token); - - P3D_token *tokens_p; - size_t num_tokens = tokens.size(); - if (!tokens.empty()) { - tokens_p = &tokens[0]; - } - - // Build up the argument list, beginning with the p3d_filename. - pvector argv; - argv.push_back(os_p3d_filename.c_str()); - for (int i = 0; i < num_args; ++i) { - argv.push_back(args[i]); - } - - P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens, - argv.size(), &argv[0], NULL); - - if (inst != NULL) { - 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); - } - - return inst; -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::delete_instance -// Access: Protected -// Description: Deletes the indicated instance and removes it from -// the internal structures. -//////////////////////////////////////////////////////////////////// -void Panda3D:: -delete_instance(P3D_instance *inst) { - P3D_instance_finish(inst); - _instances.erase(inst); - - // Make sure we also terminate any pending URLGetters associated - // with this instance. - URLGetters::iterator gi; - gi = _url_getters.begin(); - while (gi != _url_getters.end()) { - URLGetter *getter = (*gi); - if (getter->get_instance() == inst) { - URLGetters::iterator dgi = gi; - ++gi; - _url_getters.erase(dgi); - delete getter; - } - } -} - //////////////////////////////////////////////////////////////////// // Function: Panda3D::usage // Access: Protected @@ -1163,244 +784,3 @@ usage() { << " Specify the platform to masquerade as. The default is \"" << DTOOL_PLATFORM << "\" .\n\n"; } - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::parse_token -// Access: Protected -// Description: Parses a web token of the form token=value, and -// stores it in _tokens. Returns true on success, false -// on failure. -//////////////////////////////////////////////////////////////////// -bool Panda3D:: -parse_token(char *arg) { - char *equals = strchr(arg, '='); - if (equals == NULL) { - return false; - } - - // Directly munge the C string to truncate it at the equals sign. - // Classic C tricks. - *equals = '\0'; - P3D_token token; - token._keyword = strdup(arg); - token._value = strdup(equals + 1); - *equals = '='; - - _tokens.push_back(token); - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::parse_int_pair -// Access: Protected -// Description: Parses a string into an x,y pair of integers. -// Returns true on success, false on failure. -//////////////////////////////////////////////////////////////////// -bool Panda3D:: -parse_int_pair(char *arg, int &x, int &y) { - char *endptr; - x = strtol(arg, &endptr, 10); - if (*endptr == ',') { - y = strtol(endptr + 1, &endptr, 10); - if (*endptr == '\0') { - return true; - } - } - - // Some parse error on the string. - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::is_url -// 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. -//////////////////////////////////////////////////////////////////// -bool Panda3D:: -is_url(const string ¶m) { - // We define a URL prefix as a sequence of at least two letters, - // followed by a colon, followed by at least one slash. - size_t p = 0; - while (p < param.size() && isalpha(param[p])) { - ++p; - } - if (p < 2) { - // Not enough letters. - return false; - } - if (p >= param.size() || param[p] != ':') { - // No colon. - return false; - } - ++p; - if (p >= param.size() || param[p] != '/') { - // No slash. - return false; - } - - // It matches the rules. - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::report_downloading_package -// Access: Protected -// Description: Tells the user we have to download a package. -//////////////////////////////////////////////////////////////////// -void Panda3D:: -report_downloading_package(P3D_instance *instance) { - P3D_object *obj = P3D_instance_get_panda_script_object(instance); - - P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName"); - if (display_name == NULL) { - cerr << "Installing package.\n"; - return; - } - - int name_length = P3D_object_get_string(display_name, NULL, 0); - char *name = new char[name_length + 1]; - P3D_object_get_string(display_name, name, name_length + 1); - - cerr << "Installing " << name << "\n"; - - delete[] name; - P3D_object_decref(display_name); - _reporting_download = true; -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::report_download_complete -// Access: Protected -// Description: Tells the user we're done downloading packages -//////////////////////////////////////////////////////////////////// -void Panda3D:: -report_download_complete(P3D_instance *instance) { - if (_reporting_download) { - cerr << "Install complete.\n"; - } -} - -#ifdef __APPLE__ -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::st_timer_callback -// Access: Protected, Static -// Description: Installed as a timer on the event loop, so we can -// process local events, in the Apple implementation. -//////////////////////////////////////////////////////////////////// -pascal void Panda3D:: -st_timer_callback(EventLoopTimerRef timer, void *user_data) { - ((Panda3D *)user_data)->timer_callback(timer); -} -#endif // __APPLE__ - -#ifdef __APPLE__ -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::timer_callback -// Access: Protected -// Description: Installed as a timer on the event loop, so we can -// process local events, in the Apple implementation. -//////////////////////////////////////////////////////////////////// -void Panda3D:: -timer_callback(EventLoopTimerRef timer) { - // Check for new requests from the Panda3D plugin. - P3D_instance *inst = P3D_check_request(0.0); - while (inst != (P3D_instance *)NULL) { - P3D_request *request = P3D_instance_get_request(inst); - if (request != (P3D_request *)NULL) { - handle_request(request); - } - inst = P3D_check_request(0.0); - } - - // Check the download tasks. - run_getters(); - - // If we're out of instances, exit the application. - if (time_to_exit()) { - QuitApplicationEventLoop(); - } -} -#endif // __APPLE__ - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::URLGetter::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -Panda3D::URLGetter:: -URLGetter(P3D_instance *instance, int unique_id, - const URLSpec &url, const string &post_data) : - _instance(instance), - _unique_id(unique_id), - _url(url), - _post_data(post_data) -{ - HTTPClient *http = HTTPClient::get_global_ptr(); - - _channel = http->make_channel(false); - // _channel->set_download_throttle(true); - if (_post_data.empty()) { - _channel->begin_get_document(_url); - } else { - _channel->begin_post_form(_url, _post_data); - } - - _channel->download_to_ram(&_rf); - _bytes_sent = 0; -} - -//////////////////////////////////////////////////////////////////// -// Function: Panda3D::URLGetter::run -// Access: Public -// Description: Polls the URLGetter for new results. Returns true if -// the URL request is still in progress and run() should -// be called again later, or false if the URL request -// has been completed and run() should not be called -// again. -//////////////////////////////////////////////////////////////////// -bool Panda3D::URLGetter:: -run() { - if (_channel->run() || _rf.get_data_size() != 0) { - if (_rf.get_data_size() != 0) { - // Got some new data. - bool download_ok = P3D_instance_feed_url_stream - (_instance, _unique_id, P3D_RC_in_progress, - _channel->get_status_code(), - _channel->get_file_size(), - (const unsigned char *)_rf.get_data().data(), _rf.get_data_size()); - _bytes_sent += _rf.get_data_size(); - _rf.clear(); - - if (!download_ok) { - // The plugin doesn't care any more. Interrupt the download. - cerr << "Download interrupted: " << _url - << ", after " << _bytes_sent << " of " << _channel->get_file_size() - << " bytes.\n"; - return false; - } - } - - // Still more to come; call this method again later. - return true; - } - - // All done. - P3D_result_code status = P3D_RC_done; - if (!_channel->is_valid()) { - if (_channel->get_status_code() != 0) { - status = P3D_RC_http_error; - } else { - status = P3D_RC_generic_error; - } - cerr << "Error getting URL " << _url << "\n"; - } - - P3D_instance_feed_url_stream - (_instance, _unique_id, status, - _channel->get_status_code(), - _bytes_sent, NULL, 0); - return false; -} diff --git a/direct/src/plugin_standalone/panda3d.h b/direct/src/plugin_standalone/panda3d.h index 64a0494c48..15340f3013 100755 --- a/direct/src/plugin_standalone/panda3d.h +++ b/direct/src/plugin_standalone/panda3d.h @@ -15,15 +15,7 @@ #ifndef PANDA3D_H #define PANDA3D_H -// This program must link with Panda for HTTPClient support. This -// means it probably should be built with LINK_ALL_STATIC defined, so -// we won't have to deal with confusing .dll or .so files that might -// compete on the disk with the dynamically-loaded versions. There's -// no competition in memory address space, though, because -// p3d_plugin--the only file we dynamically link in--doesn't itself -// link with Panda. - -#include "pandabase.h" +#include "panda3dBase.h" #include "p3d_plugin.h" #include "httpChannel.h" #include "ramfile.h" @@ -36,12 +28,11 @@ // Description : A standalone program that invokes the Panda3D plugin // to launch .p3d files. //////////////////////////////////////////////////////////////////// -class Panda3D { +class Panda3D : private Panda3DBase { public: Panda3D(bool console_environment); int run_command_line(int argc, char *argv[]); - void run_main_loop(); protected: bool post_arg_processing(); @@ -52,44 +43,10 @@ protected: void add_mirror(string mirror_url); void choose_random_mirrors(vector_string &result, int num_mirrors); bool get_core_api(const Filename &contents_filename, TiXmlElement *xplugin); - void run_getters(); - void handle_request(P3D_request *request); - void make_parent_window(); - - P3D_instance * - create_instance(const string &p3d, bool start_instance, - int win_x, int win_y, int win_width, int win_height, - char **args, int num_args); - void delete_instance(P3D_instance *instance); void usage(); - bool parse_token(char *arg); - bool parse_int_pair(char *arg, int &x, int &y); - static bool is_url(const string ¶m); - - 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 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; string _host_url_prefix; string _download_url_prefix; @@ -98,38 +55,6 @@ protected: Mirrors _mirrors; FileSpec _core_api_dll; - bool _reporting_download; - bool _enable_security; - bool _console_environment; - - typedef pset Instances; - Instances _instances; - - typedef pvector Tokens; - Tokens _tokens; - - // This nested class keeps track of active URL requests. - class URLGetter { - public: - URLGetter(P3D_instance *instance, int unique_id, - const URLSpec &url, const string &post_data); - - bool run(); - inline P3D_instance *get_instance(); - - private: - P3D_instance *_instance; - int _unique_id; - URLSpec _url; - string _post_data; - - PT(HTTPChannel) _channel; - Ramfile _rf; - size_t _bytes_sent; - }; - - typedef pset URLGetters; - URLGetters _url_getters; }; #include "panda3d.I" diff --git a/direct/src/plugin_standalone/panda3dBase.I b/direct/src/plugin_standalone/panda3dBase.I new file mode 100755 index 0000000000..d31dea20be --- /dev/null +++ b/direct/src/plugin_standalone/panda3dBase.I @@ -0,0 +1,37 @@ +// Filename: panda3dBase.I +// Created by: pro-rsoft (07Dec09) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::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 Panda3DBase:: +time_to_exit() { + return _instances.empty() && _exit_with_last_instance; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::URLGetter::get_instance +// Access: Public +// Description: Returns the P3D_instance associated with this +// URLGetter. +//////////////////////////////////////////////////////////////////// +P3D_instance *Panda3DBase::URLGetter:: +get_instance() { + return _instance; +} diff --git a/direct/src/plugin_standalone/panda3dBase.cxx b/direct/src/plugin_standalone/panda3dBase.cxx new file mode 100644 index 0000000000..af5d883e4a --- /dev/null +++ b/direct/src/plugin_standalone/panda3dBase.cxx @@ -0,0 +1,691 @@ +// Filename: panda3dBase.cxx +// Created by: pro-rsoft (07Dec09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "panda3dBase.h" +#include "httpClient.h" +#include "find_root_dir.h" +#include "p3d_plugin_config.h" +#include "executionEnvironment.h" + +// We can include this header file to get the DTOOL_PLATFORM +// definition, even though we don't link with dtool. +#include "dtool_platform.h" +#include "pandaVersion.h" + +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +// The amount of time in seconds to wait for new messages. +static const double wait_cycle = 0.2; + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Panda3DBase:: +Panda3DBase(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: Panda3DBase::run_embedded +// Access: Public +// Description: Gets lost in the application main loop, waiting for +// system events and notifications from the open +// instance(s). +//////////////////////////////////////////////////////////////////// +int Panda3DBase:: +run_embedded(int read_offset, int argc, char *argv[]) { + if (!P3D_initialize(P3D_API_VERSION, NULL, + _host_url.c_str(), _verify_contents, _this_platform.c_str(), + _log_dirname.c_str(), _log_basename.c_str(), + !_enable_security, _console_environment)) { + // Oops, failure to initialize. + cerr << "Failed to initialize plugin (wrong API version?)\n"; + return 1; + } + + // Invoke the Core API and run the program + P3D_instance *inst = create_instance + (ExecutionEnvironment::get_binary_name(), true, + _win_x, _win_y, _win_width, _win_height, + argv, argc, read_offset); + _instances.insert(inst); + + run_main_loop(); + P3D_finalize(); + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::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 Panda3DBase:: +run_main_loop() { +#ifdef _WIN32 + if (_window_type == P3D_WT_embedded) { + // Wait for new messages from Windows, and new requests from the + // plugin. + MSG msg; + int retval; + retval = GetMessage(&msg, NULL, 0, 0); + while (retval != 0 && !time_to_exit()) { + if (retval == -1) { + cerr << "Error processing message queue.\n"; + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + + // Check for new requests from the Panda3D plugin. + P3D_instance *inst = P3D_check_request(wait_cycle); + while (inst != (P3D_instance *)NULL) { + P3D_request *request = P3D_instance_get_request(inst); + if (request != (P3D_request *)NULL) { + handle_request(request); + } + inst = P3D_check_request(wait_cycle); + } + + while (!_url_getters.empty() && + !PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + // If there are no Windows messages, check the download tasks. + run_getters(); + } + retval = GetMessage(&msg, NULL, 0, 0); + } + + // WM_QUIT has been received. Terminate all instances, and fall + // through. + while (!_instances.empty()) { + P3D_instance *inst = *(_instances.begin()); + delete_instance(inst); + } + + } else { + // Not an embedded window, so we don't have our own window to + // generate Windows events. Instead, just wait for requests. + 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); + if (request != (P3D_request *)NULL) { + handle_request(request); + } + } + run_getters(); + } + } + +#elif defined(__APPLE__) + // OSX really prefers to own the main loop, so we install a timer to + // call out to our instances and getters, rather than polling within + // the event loop as we do in the Windows case, above. + EventLoopRef main_loop = GetMainEventLoop(); + EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback); + EventLoopTimerRef timer; + EventTimerInterval interval = wait_cycle * kEventDurationSecond; + InstallEventLoopTimer(main_loop, interval, interval, + timer_upp, this, &timer); + RunApplicationEventLoop(); + RemoveEventLoopTimer(timer); + + // Terminate all instances, and fall through. + while (!_instances.empty()) { + P3D_instance *inst = *(_instances.begin()); + delete_instance(inst); + } + +#else // _WIN32, __APPLE__ + + // Now wait while we process pending requests. + 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); + if (request != (P3D_request *)NULL) { + handle_request(request); + } + } + run_getters(); + } + +#endif // _WIN32, __APPLE__ +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::run_getters +// Access: Protected +// Description: Polls all of the active URL requests. +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +run_getters() { + URLGetters::iterator gi; + gi = _url_getters.begin(); + while (gi != _url_getters.end()) { + URLGetter *getter = (*gi); + if (getter->run()) { + // This URLGetter is still working. Leave it. + ++gi; + } else { + // This URLGetter is done. Remove it and delete it. + URLGetters::iterator dgi = gi; + ++gi; + _url_getters.erase(dgi); + delete getter; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::handle_request +// Access: Protected +// Description: Handles a single request received via the plugin API +// from a p3d instance. +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +handle_request(P3D_request *request) { + bool handled = false; + switch (request->_request_type) { + case P3D_RT_stop: + delete_instance(request->_instance); +#ifdef _WIN32 + // Post a silly message to spin the event loop. + PostMessage(NULL, WM_USER, 0, 0); +#endif + handled = true; + break; + + case P3D_RT_get_url: + { + int unique_id = request->_request._get_url._unique_id; + const string &url = request->_request._get_url._url; + URLGetter *getter = new URLGetter + (request->_instance, unique_id, URLSpec(url), ""); + _url_getters.insert(getter); + handled = true; + } + break; + + case P3D_RT_notify: + { + if (strcmp(request->_request._notify._message, "ondownloadnext") == 0) { + // Tell the user we're downloading a package. + report_downloading_package(request->_instance); + } else if (strcmp(request->_request._notify._message, "ondownloadcomplete") == 0) { + // Tell the user we're done downloading. + report_download_complete(request->_instance); + } + } + break; + + default: + break; + }; + + P3D_request_finish(request, handled); +} + +#ifdef _WIN32 +LONG WINAPI +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_DESTROY: + PostQuitMessage(0); + break; + }; + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::make_parent_window +// Access: Protected +// Description: Creates a toplevel window to contain the embedded +// instances. Windows implementation. +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +make_parent_window() { + WNDCLASS wc; + + HINSTANCE application = GetModuleHandle(NULL); + ZeroMemory(&wc, sizeof(WNDCLASS)); + wc.lpfnWndProc = window_proc; + wc.hInstance = application; + wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wc.lpszClassName = "panda3d"; + + if (!RegisterClass(&wc)) { + cerr << "Could not register window class!\n"; + exit(1); + } + + DWORD window_style = + WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | + WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_SIZEBOX | WS_MAXIMIZEBOX; + + HWND toplevel_window = + CreateWindow("panda3d", "Panda3D", window_style, + CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height, + NULL, NULL, application, 0); + if (!toplevel_window) { + cerr << "Could not create toplevel window!\n"; + exit(1); + } + + ShowWindow(toplevel_window, SW_SHOWNORMAL); + + memset(&_parent_window, 0, sizeof(_parent_window)); + _parent_window._window_handle_type = P3D_WHT_win_hwnd; + _parent_window._handle._win_hwnd._hwnd = toplevel_window; +} + +#else + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::make_parent_window +// Access: Protected +// Description: Creates a toplevel window to contain the embedded +// instances. +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +make_parent_window() { + // TODO. + assert(false); +} + +#endif + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::create_instance +// Access: Protected +// Description: Uses the plugin API to create a new P3D instance to +// 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 *Panda3DBase:: +create_instance(const string &p3d, bool start_instance, + int win_x, int win_y, int win_width, int win_height, + char **args, int num_args, const int &p3d_offset) { + // 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 && start_instance) { + if (!p3d_filename.exists()) { + cerr << "No such file: " << p3d_filename << "\n"; + exit(1); + } + + p3d_filename.make_absolute(); + os_p3d_filename = p3d_filename.to_os_specific(); + } + + // Build up the token list. + Tokens tokens = _tokens; + P3D_token token; + + string log_basename; + if (!_log_dirname.empty()) { + // Generate output to a logfile. + log_basename = p3d_filename.get_basename_wo_extension(); + token._keyword = "log_basename"; + token._value = log_basename.c_str(); + tokens.push_back(token); + } else { + // Send output to the console. + token._keyword = "console_output"; + if (_console_environment) { + token._value = "1"; + } else { + token._value = "0"; + } + tokens.push_back(token); + } + + token._keyword = "auto_start"; + token._value = "1"; + tokens.push_back(token); + + P3D_token *tokens_p; + size_t num_tokens = tokens.size(); + if (!tokens.empty()) { + tokens_p = &tokens[0]; + } + + // Build up the argument list, beginning with the p3d_filename. + pvector argv; + argv.push_back(os_p3d_filename.c_str()); + for (int i = 0; i < num_args; ++i) { + argv.push_back(args[i]); + } + + P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens, + argv.size(), &argv[0], NULL); + + if (inst != NULL) { + 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_offset); + } + + P3D_instance_setup_window + (inst, _window_type, _win_x, _win_y, _win_width, _win_height, &_parent_window); + } + + return inst; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::delete_instance +// Access: Protected +// Description: Deletes the indicated instance and removes it from +// the internal structures. +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +delete_instance(P3D_instance *inst) { + P3D_instance_finish(inst); + _instances.erase(inst); + + // Make sure we also terminate any pending URLGetters associated + // with this instance. + URLGetters::iterator gi; + gi = _url_getters.begin(); + while (gi != _url_getters.end()) { + URLGetter *getter = (*gi); + if (getter->get_instance() == inst) { + URLGetters::iterator dgi = gi; + ++gi; + _url_getters.erase(dgi); + delete getter; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::parse_token +// Access: Protected +// Description: Parses a web token of the form token=value, and +// stores it in _tokens. Returns true on success, false +// on failure. +//////////////////////////////////////////////////////////////////// +bool Panda3DBase:: +parse_token(char *arg) { + char *equals = strchr(arg, '='); + if (equals == NULL) { + return false; + } + + // Directly munge the C string to truncate it at the equals sign. + // Classic C tricks. + *equals = '\0'; + P3D_token token; + token._keyword = strdup(arg); + token._value = strdup(equals + 1); + *equals = '='; + + _tokens.push_back(token); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::parse_int_pair +// Access: Protected +// Description: Parses a string into an x,y pair of integers. +// Returns true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool Panda3DBase:: +parse_int_pair(char *arg, int &x, int &y) { + char *endptr; + x = strtol(arg, &endptr, 10); + if (*endptr == ',') { + y = strtol(endptr + 1, &endptr, 10); + if (*endptr == '\0') { + return true; + } + } + + // Some parse error on the string. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::is_url +// 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. +//////////////////////////////////////////////////////////////////// +bool Panda3DBase:: +is_url(const string ¶m) { + // We define a URL prefix as a sequence of at least two letters, + // followed by a colon, followed by at least one slash. + size_t p = 0; + while (p < param.size() && isalpha(param[p])) { + ++p; + } + if (p < 2) { + // Not enough letters. + return false; + } + if (p >= param.size() || param[p] != ':') { + // No colon. + return false; + } + ++p; + if (p >= param.size() || param[p] != '/') { + // No slash. + return false; + } + + // It matches the rules. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::report_downloading_package +// Access: Protected +// Description: Tells the user we have to download a package. +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +report_downloading_package(P3D_instance *instance) { + P3D_object *obj = P3D_instance_get_panda_script_object(instance); + + P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName"); + if (display_name == NULL) { + cerr << "Installing package.\n"; + return; + } + + int name_length = P3D_object_get_string(display_name, NULL, 0); + char *name = new char[name_length + 1]; + P3D_object_get_string(display_name, name, name_length + 1); + + cerr << "Installing " << name << "\n"; + + delete[] name; + P3D_object_decref(display_name); + _reporting_download = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::report_download_complete +// Access: Protected +// Description: Tells the user we're done downloading packages +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +report_download_complete(P3D_instance *instance) { + if (_reporting_download) { + cerr << "Install complete.\n"; + } +} + +#ifdef __APPLE__ +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::st_timer_callback +// Access: Protected, Static +// Description: Installed as a timer on the event loop, so we can +// process local events, in the Apple implementation. +//////////////////////////////////////////////////////////////////// +pascal void Panda3DBase:: +st_timer_callback(EventLoopTimerRef timer, void *user_data) { + ((Panda3D *)user_data)->timer_callback(timer); +} +#endif // __APPLE__ + +#ifdef __APPLE__ +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::timer_callback +// Access: Protected +// Description: Installed as a timer on the event loop, so we can +// process local events, in the Apple implementation. +//////////////////////////////////////////////////////////////////// +void Panda3DBase:: +timer_callback(EventLoopTimerRef timer) { + // Check for new requests from the Panda3D plugin. + P3D_instance *inst = P3D_check_request(0.0); + while (inst != (P3D_instance *)NULL) { + P3D_request *request = P3D_instance_get_request(inst); + if (request != (P3D_request *)NULL) { + handle_request(request); + } + inst = P3D_check_request(0.0); + } + + // Check the download tasks. + run_getters(); + + // If we're out of instances, exit the application. + if (time_to_exit()) { + QuitApplicationEventLoop(); + } +} +#endif // __APPLE__ + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::URLGetter::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Panda3DBase::URLGetter:: +URLGetter(P3D_instance *instance, int unique_id, + const URLSpec &url, const string &post_data) : + _instance(instance), + _unique_id(unique_id), + _url(url), + _post_data(post_data) +{ + HTTPClient *http = HTTPClient::get_global_ptr(); + + _channel = http->make_channel(false); + // _channel->set_download_throttle(true); + if (_post_data.empty()) { + _channel->begin_get_document(_url); + } else { + _channel->begin_post_form(_url, _post_data); + } + + _channel->download_to_ram(&_rf); + _bytes_sent = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Panda3DBase::URLGetter::run +// Access: Public +// Description: Polls the URLGetter for new results. Returns true if +// the URL request is still in progress and run() should +// be called again later, or false if the URL request +// has been completed and run() should not be called +// again. +//////////////////////////////////////////////////////////////////// +bool Panda3DBase::URLGetter:: +run() { + if (_channel->run() || _rf.get_data_size() != 0) { + if (_rf.get_data_size() != 0) { + // Got some new data. + bool download_ok = P3D_instance_feed_url_stream + (_instance, _unique_id, P3D_RC_in_progress, + _channel->get_status_code(), + _channel->get_file_size(), + (const unsigned char *)_rf.get_data().data(), _rf.get_data_size()); + _bytes_sent += _rf.get_data_size(); + _rf.clear(); + + if (!download_ok) { + // The plugin doesn't care any more. Interrupt the download. + cerr << "Download interrupted: " << _url + << ", after " << _bytes_sent << " of " << _channel->get_file_size() + << " bytes.\n"; + return false; + } + } + + // Still more to come; call this method again later. + return true; + } + + // All done. + P3D_result_code status = P3D_RC_done; + if (!_channel->is_valid()) { + if (_channel->get_status_code() != 0) { + status = P3D_RC_http_error; + } else { + status = P3D_RC_generic_error; + } + cerr << "Error getting URL " << _url << "\n"; + } + + P3D_instance_feed_url_stream + (_instance, _unique_id, status, + _channel->get_status_code(), + _bytes_sent, NULL, 0); + return false; +} diff --git a/direct/src/plugin_standalone/panda3dBase.h b/direct/src/plugin_standalone/panda3dBase.h new file mode 100755 index 0000000000..b529bed1d8 --- /dev/null +++ b/direct/src/plugin_standalone/panda3dBase.h @@ -0,0 +1,121 @@ +// Filename: panda3dBase.h +// Created by: pro-rsoft (07Dec09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 PANDA3DBASE_H +#define PANDA3DBASE_H + +// This program must link with Panda for HTTPClient support. This +// means it probably should be built with LINK_ALL_STATIC defined, so +// we won't have to deal with confusing .dll or .so files that might +// compete on the disk with the dynamically-loaded versions. There's +// no competition in memory address space, though, because +// p3d_plugin--the only file we dynamically link in--doesn't itself +// link with Panda. + +#include "pandabase.h" +#include "p3d_plugin.h" +#include "httpChannel.h" +#include "ramfile.h" +#include "fileSpec.h" +#include "pset.h" +#include "vector_string.h" + +//////////////////////////////////////////////////////////////////// +// Class : Panda3DBase +// Description : Base for creating a standalone application that +// invokes the panda3d plugin to launch .p3d files. +//////////////////////////////////////////////////////////////////// +class Panda3DBase { +public: + Panda3DBase(bool console_environment); + + int run_embedded(int read_offset, int argc, char *argv[]); + void run_main_loop(); + +protected: + void run_getters(); + void handle_request(P3D_request *request); + void make_parent_window(); + + P3D_instance * + create_instance(const string &p3d, bool start_instance, + int win_x, int win_y, int win_width, int win_height, + char **args, int num_args, const int &p3d_offset = 0); + void delete_instance(P3D_instance *instance); + + bool parse_token(char *arg); + bool parse_int_pair(char *arg, int &x, int &y); + static bool is_url(const string ¶m); + + 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 + +protected: + string _host_url; + 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; + + bool _reporting_download; + bool _enable_security; + bool _console_environment; + + typedef pset Instances; + Instances _instances; + + typedef pvector Tokens; + Tokens _tokens; + + // This nested class keeps track of active URL requests. + class URLGetter { + public: + URLGetter(P3D_instance *instance, int unique_id, + const URLSpec &url, const string &post_data); + + bool run(); + inline P3D_instance *get_instance(); + + private: + P3D_instance *_instance; + int _unique_id; + URLSpec _url; + string _post_data; + + PT(HTTPChannel) _channel; + Ramfile _rf; + size_t _bytes_sent; + }; + + typedef pset URLGetters; + URLGetters _url_getters; +}; + +#include "panda3dBase.I" + +#endif +