Browse Source

restructure for OOP, use polling instead of threads

David Rose 16 years ago
parent
commit
38c00a66f9
1 changed files with 380 additions and 308 deletions
  1. 380 308
      direct/src/plugin_standalone/panda3d.cxx

+ 380 - 308
direct/src/plugin_standalone/panda3d.cxx

@@ -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);
 }
 }