|
@@ -12,31 +12,14 @@
|
|
|
//
|
|
//
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
-// 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 "panda3d.h"
|
|
|
|
|
+#include "httpClient.h"
|
|
|
|
|
+#include "load_plugin.h"
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
#ifdef _WIN32
|
|
|
#include <windows.h>
|
|
#include <windows.h>
|
|
|
-#else
|
|
|
|
|
-#include <dlfcn.h>
|
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
-#include "httpClient.h"
|
|
|
|
|
-#include "httpChannel.h"
|
|
|
|
|
-#include "ramfile.h"
|
|
|
|
|
-#include "thread.h"
|
|
|
|
|
-#include "pset.h"
|
|
|
|
|
-
|
|
|
|
|
-#include "p3d_plugin.h"
|
|
|
|
|
-#include "load_plugin.h"
|
|
|
|
|
-
|
|
|
|
|
#ifndef HAVE_GETOPT
|
|
#ifndef HAVE_GETOPT
|
|
|
#include "gnu_getopt.h"
|
|
#include "gnu_getopt.h"
|
|
|
#else
|
|
#else
|
|
@@ -45,104 +28,272 @@
|
|
|
#endif
|
|
#endif
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
-typedef pset<P3D_instance *> Instances;
|
|
|
|
|
-Instances _instances;
|
|
|
|
|
-
|
|
|
|
|
-class URLGetterThread : public Thread {
|
|
|
|
|
-public:
|
|
|
|
|
- URLGetterThread(P3D_instance *instance,
|
|
|
|
|
- int unique_id,
|
|
|
|
|
- const URLSpec &url,
|
|
|
|
|
- const string &post_data);
|
|
|
|
|
-protected:
|
|
|
|
|
- virtual void thread_main();
|
|
|
|
|
-
|
|
|
|
|
-private:
|
|
|
|
|
- P3D_instance *_instance;
|
|
|
|
|
- int _unique_id;
|
|
|
|
|
- URLSpec _url;
|
|
|
|
|
- string _post_data;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-URLGetterThread::
|
|
|
|
|
-URLGetterThread(P3D_instance *instance,
|
|
|
|
|
- int unique_id,
|
|
|
|
|
- const URLSpec &url,
|
|
|
|
|
- const string &post_data) :
|
|
|
|
|
- Thread(url, "URLGetter"),
|
|
|
|
|
- _instance(instance),
|
|
|
|
|
- _unique_id(unique_id),
|
|
|
|
|
- _url(url),
|
|
|
|
|
- _post_data(post_data)
|
|
|
|
|
-{
|
|
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::Constructor
|
|
|
|
|
+// Access: Public
|
|
|
|
|
+// Description:
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+Panda3D::
|
|
|
|
|
+Panda3D() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void URLGetterThread::
|
|
|
|
|
-thread_main() {
|
|
|
|
|
- HTTPClient *http = HTTPClient::get_global_ptr();
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::run
|
|
|
|
|
+// Access: Public
|
|
|
|
|
+// Description: Starts the program going. Returns 0 on success,
|
|
|
|
|
+// nonzero on failure.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+int Panda3D::
|
|
|
|
|
+run(int argc, char *argv[]) {
|
|
|
|
|
+ extern char *optarg;
|
|
|
|
|
+ extern int optind;
|
|
|
|
|
+ const char *optstr = "p:l:t:s:o:h";
|
|
|
|
|
|
|
|
- cerr << "Getting URL " << _url << "\n";
|
|
|
|
|
|
|
+ Filename p3d_plugin_filename;
|
|
|
|
|
+ Filename output_filename;
|
|
|
|
|
+ P3D_window_type window_type = P3D_WT_toplevel;
|
|
|
|
|
+ int win_x = 0, win_y = 0;
|
|
|
|
|
+ int win_width = 0, win_height = 0;
|
|
|
|
|
|
|
|
- PT(HTTPChannel) channel = http->make_channel(false);
|
|
|
|
|
- if (_post_data.empty()) {
|
|
|
|
|
- channel->begin_get_document(_url);
|
|
|
|
|
- } else {
|
|
|
|
|
- channel->begin_post_form(_url, _post_data);
|
|
|
|
|
|
|
+ int flag = getopt(argc, argv, optstr);
|
|
|
|
|
+
|
|
|
|
|
+ while (flag != EOF) {
|
|
|
|
|
+ switch (flag) {
|
|
|
|
|
+ case 'p':
|
|
|
|
|
+ p3d_plugin_filename = Filename::from_os_specific(optarg);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'l':
|
|
|
|
|
+ output_filename = Filename::from_os_specific(optarg);
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 't':
|
|
|
|
|
+ if (strcmp(optarg, "toplevel") == 0) {
|
|
|
|
|
+ window_type = P3D_WT_toplevel;
|
|
|
|
|
+ } else if (strcmp(optarg, "embedded") == 0) {
|
|
|
|
|
+ window_type = P3D_WT_embedded;
|
|
|
|
|
+ } else if (strcmp(optarg, "fullscreen") == 0) {
|
|
|
|
|
+ window_type = P3D_WT_fullscreen;
|
|
|
|
|
+ } else if (strcmp(optarg, "hidden") == 0) {
|
|
|
|
|
+ window_type = P3D_WT_hidden;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ cerr << "Invalid value for -t: " << optarg << "\n";
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 's':
|
|
|
|
|
+ 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)) {
|
|
|
|
|
+ cerr << "Invalid value for -o: " << optarg << "\n";
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case 'h':
|
|
|
|
|
+ case '?':
|
|
|
|
|
+ default:
|
|
|
|
|
+ usage();
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ flag = getopt(argc, argv, optstr);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- Ramfile rf;
|
|
|
|
|
- channel->download_to_ram(&rf);
|
|
|
|
|
|
|
+ argc -= (optind-1);
|
|
|
|
|
+ argv += (optind-1);
|
|
|
|
|
|
|
|
- size_t bytes_sent = 0;
|
|
|
|
|
- while (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 (argc < 2) {
|
|
|
|
|
+ usage();
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- 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;
|
|
|
|
|
|
|
+ if (!load_plugin(p3d_plugin_filename.to_os_specific())) {
|
|
|
|
|
+ cerr << "Unable to load Panda3D plugin.\n";
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int num_instances = argc - 1;
|
|
|
|
|
+
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ make_parent_window(parent_window, win_width, win_height);
|
|
|
|
|
+
|
|
|
|
|
+ // 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);
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ // Subdivide the window into num_x_spans * num_y_spans sub-windows.
|
|
|
|
|
+ int num_y_spans = int(sqrt((double)num_instances));
|
|
|
|
|
+ int num_x_spans = (num_instances + 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_instances) {
|
|
|
|
|
+ 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
|
|
|
|
|
+ (argv[i + 1], P3D_WT_embedded,
|
|
|
|
|
+ inst_x, inst_y, inst_width, inst_height, parent_window,
|
|
|
|
|
+ output_filename);
|
|
|
|
|
+ _instances.insert(inst);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Not an embedded window. Create each window with the same parameters.
|
|
|
|
|
+ for (int i = 0; i < num_instances; ++i) {
|
|
|
|
|
+ P3D_instance *inst = create_instance
|
|
|
|
|
+ (argv[i + 1], window_type,
|
|
|
|
|
+ win_x, win_y, win_width, win_height, parent_window,
|
|
|
|
|
+ output_filename);
|
|
|
|
|
+ _instances.insert(inst);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 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;
|
|
|
|
|
|
|
+#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 && !_instances.empty()) {
|
|
|
|
|
+ if (retval == -1) {
|
|
|
|
|
+ cerr << "Error processing message queue.\n";
|
|
|
|
|
+ exit(1);
|
|
|
|
|
+ }
|
|
|
|
|
+ TranslateMessage(&msg);
|
|
|
|
|
+ DispatchMessage(&msg);
|
|
|
|
|
+
|
|
|
|
|
+ // Check for new requests from the Panda3D plugin.
|
|
|
|
|
+ P3D_instance *inst = P3D_check_request(false);
|
|
|
|
|
+ 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(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
}
|
|
}
|
|
|
- cerr << "Error getting URL " << _url << "\n";
|
|
|
|
|
|
|
+
|
|
|
|
|
+ cerr << "WM_QUIT\n";
|
|
|
|
|
+ // WM_QUIT has been received. Terminate all instances, and fall
|
|
|
|
|
+ // through.
|
|
|
|
|
+ while (!_instances.empty()) {
|
|
|
|
|
+ P3D_instance *inst = *(_instances.begin());
|
|
|
|
|
+ delete_instance(inst);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
} else {
|
|
} else {
|
|
|
- cerr << "Done getting URL " << _url << ", got " << bytes_sent << " bytes\n";
|
|
|
|
|
|
|
+ // Not an embedded window, so we don't have our own window to
|
|
|
|
|
+ // generate Windows events. Instead, just wait for requests.
|
|
|
|
|
+ while (!_instances.empty()) {
|
|
|
|
|
+ P3D_instance *inst = P3D_check_request(false);
|
|
|
|
|
+ if (inst != (P3D_instance *)NULL) {
|
|
|
|
|
+ P3D_request *request = P3D_instance_get_request(inst);
|
|
|
|
|
+ if (request != (P3D_request *)NULL) {
|
|
|
|
|
+ handle_request(request);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ run_getters();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
- P3D_instance_feed_url_stream
|
|
|
|
|
- (_instance, _unique_id, status,
|
|
|
|
|
- channel->get_status_code(),
|
|
|
|
|
- bytes_sent, NULL, 0);
|
|
|
|
|
|
|
+ // Now wait while we process pending requests.
|
|
|
|
|
+ while (!_instances.empty()) {
|
|
|
|
|
+ P3D_instance *inst = P3D_check_request(false);
|
|
|
|
|
+ if (inst != (P3D_instance *)NULL) {
|
|
|
|
|
+ P3D_request *request = P3D_instance_get_request(inst);
|
|
|
|
|
+ if (request != (P3D_request *)NULL) {
|
|
|
|
|
+ handle_request(request);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ run_getters();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // All instances have finished; we can exit.
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::run_getters
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// 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;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::handle_request
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description: Handles a single request received via the plugin API
|
|
|
|
|
+// from a p3d instance.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+void Panda3D::
|
|
|
handle_request(P3D_request *request) {
|
|
handle_request(P3D_request *request) {
|
|
|
bool handled = false;
|
|
bool handled = false;
|
|
|
|
|
|
|
|
switch (request->_request_type) {
|
|
switch (request->_request_type) {
|
|
|
case P3D_RT_stop:
|
|
case P3D_RT_stop:
|
|
|
cerr << "Got P3D_RT_stop\n";
|
|
cerr << "Got P3D_RT_stop\n";
|
|
|
- P3D_instance_finish(request->_instance);
|
|
|
|
|
- _instances.erase(request->_instance);
|
|
|
|
|
|
|
+ delete_instance(request->_instance);
|
|
|
#ifdef _WIN32
|
|
#ifdef _WIN32
|
|
|
// Post a silly message to spin the event loop.
|
|
// Post a silly message to spin the event loop.
|
|
|
PostMessage(NULL, WM_USER, 0, 0);
|
|
PostMessage(NULL, WM_USER, 0, 0);
|
|
@@ -153,21 +304,21 @@ handle_request(P3D_request *request) {
|
|
|
case P3D_RT_get_url:
|
|
case P3D_RT_get_url:
|
|
|
cerr << "Got P3D_RT_get_url\n";
|
|
cerr << "Got P3D_RT_get_url\n";
|
|
|
{
|
|
{
|
|
|
- PT(URLGetterThread) thread = new URLGetterThread
|
|
|
|
|
|
|
+ URLGetter *getter = new URLGetter
|
|
|
(request->_instance, request->_request._get_url._unique_id,
|
|
(request->_instance, request->_request._get_url._unique_id,
|
|
|
URLSpec(request->_request._get_url._url), "");
|
|
URLSpec(request->_request._get_url._url), "");
|
|
|
- thread->start(TP_normal, false);
|
|
|
|
|
|
|
+ _url_getters.insert(getter);
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
case P3D_RT_post_url:
|
|
case P3D_RT_post_url:
|
|
|
cerr << "Got P3D_RT_post_url\n";
|
|
cerr << "Got P3D_RT_post_url\n";
|
|
|
{
|
|
{
|
|
|
- PT(URLGetterThread) thread = new URLGetterThread
|
|
|
|
|
|
|
+ URLGetter *getter = new URLGetter
|
|
|
(request->_instance, request->_request._post_url._unique_id,
|
|
(request->_instance, request->_request._post_url._unique_id,
|
|
|
URLSpec(request->_request._post_url._url),
|
|
URLSpec(request->_request._post_url._url),
|
|
|
string(request->_request._post_url._post_data, request->_request._post_url._post_data_size));
|
|
string(request->_request._post_url._post_data, request->_request._post_url._post_data_size));
|
|
|
- thread->start(TP_normal, false);
|
|
|
|
|
|
|
+ _url_getters.insert(getter);
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
@@ -196,7 +347,13 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|
|
return DefWindowProc(hwnd, msg, wparam, lparam);
|
|
return DefWindowProc(hwnd, msg, wparam, lparam);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::make_parent_window
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description: Creates a toplevel window to contain the embedded
|
|
|
|
|
+// instances. Windows implementation.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+void Panda3D::
|
|
|
make_parent_window(P3D_window_handle &parent_window,
|
|
make_parent_window(P3D_window_handle &parent_window,
|
|
|
int win_width, int win_height) {
|
|
int win_width, int win_height) {
|
|
|
WNDCLASS wc;
|
|
WNDCLASS wc;
|
|
@@ -235,7 +392,13 @@ make_parent_window(P3D_window_handle &parent_window,
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::make_parent_window
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description: Creates a toplevel window to contain the embedded
|
|
|
|
|
+// instances. OS X implementation.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+void Panda3D::
|
|
|
make_parent_window(P3D_window_handle &parent_window,
|
|
make_parent_window(P3D_window_handle &parent_window,
|
|
|
int win_width, int win_height) {
|
|
int win_width, int win_height) {
|
|
|
// TODO.
|
|
// TODO.
|
|
@@ -244,7 +407,13 @@ make_parent_window(P3D_window_handle &parent_window,
|
|
|
|
|
|
|
|
#endif // __APPLE__
|
|
#endif // __APPLE__
|
|
|
|
|
|
|
|
-P3D_instance *
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::create_instance
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// Description: Uses the plugin API to create a new P3D instance to
|
|
|
|
|
+// play a particular .p3d file.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+P3D_instance *Panda3D::
|
|
|
create_instance(const string &arg, P3D_window_type window_type,
|
|
create_instance(const string &arg, P3D_window_type window_type,
|
|
|
int win_x, int win_y, int win_width, int win_height,
|
|
int win_x, int win_y, int win_width, int win_height,
|
|
|
P3D_window_handle parent_window,
|
|
P3D_window_handle parent_window,
|
|
@@ -278,7 +447,38 @@ create_instance(const string &arg, P3D_window_type window_type,
|
|
|
return inst;
|
|
return inst;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::delete_instance
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// 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: Private
|
|
|
|
|
+// Description: Reports the available command-line options.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+void Panda3D::
|
|
|
usage() {
|
|
usage() {
|
|
|
cerr
|
|
cerr
|
|
|
<< "\nUsage:\n"
|
|
<< "\nUsage:\n"
|
|
@@ -312,7 +512,13 @@ usage() {
|
|
|
<< " screen, or on the parent window.\n\n";
|
|
<< " screen, or on the parent window.\n\n";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bool
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// Function: Panda3D::parse_int_pair
|
|
|
|
|
+// Access: Private
|
|
|
|
|
+// 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) {
|
|
parse_int_pair(char *arg, int &x, int &y) {
|
|
|
char *endptr;
|
|
char *endptr;
|
|
|
x = strtol(arg, &endptr, 10);
|
|
x = strtol(arg, &endptr, 10);
|
|
@@ -327,226 +533,92 @@ parse_int_pair(char *arg, int &x, int &y) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-int
|
|
|
|
|
-main(int argc, char *argv[]) {
|
|
|
|
|
-/*
|
|
|
|
|
-int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
|
|
|
|
|
- char *targv[] = {
|
|
|
|
|
- "panda3d",
|
|
|
|
|
- "-tembedded",
|
|
|
|
|
- "c:/cygwin/home/drose/ralph.p3d",
|
|
|
|
|
- NULL,
|
|
|
|
|
- };
|
|
|
|
|
- char **argv = targv;
|
|
|
|
|
- int argc = 3;
|
|
|
|
|
-*/
|
|
|
|
|
-
|
|
|
|
|
- extern char *optarg;
|
|
|
|
|
- extern int optind;
|
|
|
|
|
- const char *optstr = "p:l:t:s:o:h";
|
|
|
|
|
-
|
|
|
|
|
- Filename p3d_plugin_filename;
|
|
|
|
|
- Filename output_filename;
|
|
|
|
|
- P3D_window_type window_type = P3D_WT_toplevel;
|
|
|
|
|
- int win_x = 0, win_y = 0;
|
|
|
|
|
- int win_width = 0, win_height = 0;
|
|
|
|
|
-
|
|
|
|
|
- int flag = getopt(argc, argv, optstr);
|
|
|
|
|
-
|
|
|
|
|
- while (flag != EOF) {
|
|
|
|
|
- switch (flag) {
|
|
|
|
|
- case 'p':
|
|
|
|
|
- p3d_plugin_filename = Filename::from_os_specific(optarg);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case 'l':
|
|
|
|
|
- output_filename = Filename::from_os_specific(optarg);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case 't':
|
|
|
|
|
- if (strcmp(optarg, "toplevel") == 0) {
|
|
|
|
|
- window_type = P3D_WT_toplevel;
|
|
|
|
|
- } else if (strcmp(optarg, "embedded") == 0) {
|
|
|
|
|
- window_type = P3D_WT_embedded;
|
|
|
|
|
- } else if (strcmp(optarg, "fullscreen") == 0) {
|
|
|
|
|
- window_type = P3D_WT_fullscreen;
|
|
|
|
|
- } else if (strcmp(optarg, "hidden") == 0) {
|
|
|
|
|
- window_type = P3D_WT_hidden;
|
|
|
|
|
- } else {
|
|
|
|
|
- cerr << "Invalid value for -t: " << optarg << "\n";
|
|
|
|
|
- return 1;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case 's':
|
|
|
|
|
- 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)) {
|
|
|
|
|
- cerr << "Invalid value for -o: " << optarg << "\n";
|
|
|
|
|
- return 1;
|
|
|
|
|
- }
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case 'h':
|
|
|
|
|
- case '?':
|
|
|
|
|
- default:
|
|
|
|
|
- usage();
|
|
|
|
|
- return 1;
|
|
|
|
|
- }
|
|
|
|
|
- flag = getopt(argc, argv, optstr);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- argc -= (optind-1);
|
|
|
|
|
- argv += (optind-1);
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// 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();
|
|
|
|
|
|
|
|
- if (argc < 2) {
|
|
|
|
|
- usage();
|
|
|
|
|
- return 1;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ cerr << "Getting URL " << _url << "\n";
|
|
|
|
|
|
|
|
- if (!load_plugin(p3d_plugin_filename.to_os_specific())) {
|
|
|
|
|
- cerr << "Unable to load Panda3D plugin.\n";
|
|
|
|
|
- return 1;
|
|
|
|
|
|
|
+ _channel = http->make_channel(false);
|
|
|
|
|
+ if (_post_data.empty()) {
|
|
|
|
|
+ _channel->begin_get_document(_url);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ _channel->begin_post_form(_url, _post_data);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- int num_instances = argc - 1;
|
|
|
|
|
-
|
|
|
|
|
- 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;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- make_parent_window(parent_window, win_width, win_height);
|
|
|
|
|
-
|
|
|
|
|
- // 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);
|
|
|
|
|
-#endif
|
|
|
|
|
-
|
|
|
|
|
- // Subdivide the window into num_x_spans * num_y_spans sub-windows.
|
|
|
|
|
- int num_y_spans = int(sqrt((double)num_instances));
|
|
|
|
|
- int num_x_spans = (num_instances + 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_instances) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ _channel->download_to_ram(&_rf);
|
|
|
|
|
+ _bytes_sent = 0;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- // Create instance i at window slot (xi, yi).
|
|
|
|
|
- int inst_x = win_x + xi * inst_width;
|
|
|
|
|
- int inst_y = win_y + yi * inst_height;
|
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+// 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();
|
|
|
|
|
|
|
|
- P3D_instance *inst = create_instance
|
|
|
|
|
- (argv[i + 1], P3D_WT_embedded,
|
|
|
|
|
- inst_x, inst_y, inst_width, inst_height, parent_window,
|
|
|
|
|
- output_filename);
|
|
|
|
|
- _instances.insert(inst);
|
|
|
|
|
|
|
+ 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;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- } else {
|
|
|
|
|
- // Not an embedded window. Create each window with the same parameters.
|
|
|
|
|
- for (int i = 0; i < num_instances; ++i) {
|
|
|
|
|
- P3D_instance *inst = create_instance
|
|
|
|
|
- (argv[i + 1], window_type,
|
|
|
|
|
- win_x, win_y, win_width, win_height, parent_window,
|
|
|
|
|
- output_filename);
|
|
|
|
|
- _instances.insert(inst);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Still more to come; call this method again later.
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-#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 && !_instances.empty()) {
|
|
|
|
|
- if (retval == -1) {
|
|
|
|
|
- cerr << "Error processing message queue.\n";
|
|
|
|
|
- exit(1);
|
|
|
|
|
- }
|
|
|
|
|
- TranslateMessage(&msg);
|
|
|
|
|
- DispatchMessage(&msg);
|
|
|
|
|
-
|
|
|
|
|
- // Check for new requests from the Panda3D plugin.
|
|
|
|
|
- P3D_instance *inst = P3D_check_request(false);
|
|
|
|
|
- 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(false);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- while (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
|
|
|
|
|
- // spin, so we don't starve the download threads.
|
|
|
|
|
- // TODO: use a better mechanism here.
|
|
|
|
|
- Thread::force_yield();
|
|
|
|
|
- }
|
|
|
|
|
- retval = GetMessage(&msg, NULL, 0, 0);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- cerr << "WM_QUIT\n";
|
|
|
|
|
- // WM_QUIT has been received. Terminate all instances, and fall
|
|
|
|
|
- // through.
|
|
|
|
|
- Instances::iterator ii;
|
|
|
|
|
- for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
|
|
|
|
|
- P3D_instance_finish(*ii);
|
|
|
|
|
|
|
+ // 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;
|
|
|
}
|
|
}
|
|
|
- _instances.clear();
|
|
|
|
|
-
|
|
|
|
|
|
|
+ cerr << "Error getting URL " << _url << "\n";
|
|
|
} else {
|
|
} 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()) {
|
|
|
|
|
- P3D_instance *inst = P3D_check_request(false);
|
|
|
|
|
- if (inst != (P3D_instance *)NULL) {
|
|
|
|
|
- P3D_request *request = P3D_instance_get_request(inst);
|
|
|
|
|
- if (request != (P3D_request *)NULL) {
|
|
|
|
|
- handle_request(request);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- Thread::force_yield();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ cerr << "Done getting URL " << _url << ", got " << _bytes_sent << " bytes\n";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-#endif
|
|
|
|
|
|
|
|
|
|
- // Now wait while we process pending requests.
|
|
|
|
|
- while (!_instances.empty()) {
|
|
|
|
|
- P3D_instance *inst = P3D_check_request(false);
|
|
|
|
|
- if (inst != (P3D_instance *)NULL) {
|
|
|
|
|
- P3D_request *request = P3D_instance_get_request(inst);
|
|
|
|
|
- if (request != (P3D_request *)NULL) {
|
|
|
|
|
- handle_request(request);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- Thread::force_yield();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ P3D_instance_feed_url_stream
|
|
|
|
|
+ (_instance, _unique_id, status,
|
|
|
|
|
+ _channel->get_status_code(),
|
|
|
|
|
+ _bytes_sent, NULL, 0);
|
|
|
|
|
+ return false;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- // All instances have finished; we can exit.
|
|
|
|
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
|
+int
|
|
|
|
|
+main(int argc, char *argv[]) {
|
|
|
|
|
+ Panda3D program;
|
|
|
|
|
+ return program.run(argc, argv);
|
|
|
}
|
|
}
|