Browse Source

twirling icon for ActiveX too

David Rose 14 years ago
parent
commit
1d60925220

+ 1 - 1
direct/src/plugin/load_plugin.cxx

@@ -388,7 +388,7 @@ init_plugin(const string &contents_filename, const string &host_url,
 //               space and clears all of the pointers.
 ////////////////////////////////////////////////////////////////////
 void
-unload_plugin() {
+unload_plugin(ostream &logfile) {
   if (!plugin_loaded) {
     return;
   }

+ 1 - 1
direct/src/plugin/load_plugin.h

@@ -75,7 +75,7 @@ init_plugin(const string &contents_filename, const string &host_url,
             bool trusted_environment, bool console_environment,
             const string &root_dir, const string &host_dir, ostream &logfile);
 
-void unload_plugin();
+void unload_plugin(ostream &logfile);
 bool is_plugin_loaded();
 
 #endif

+ 147 - 7
direct/src/plugin_activex/P3DActiveXCtrl.cpp

@@ -163,7 +163,10 @@ CP3DActiveXCtrl::CP3DActiveXCtrl() : m_instance( *this ), m_pPandaObject( NULL )
 {
     InitializeIIDs(&IID_DP3DActiveX, &IID_DP3DActiveXEvents);
     // TODO: Initialize your control's instance data here.
+    _state = S_init;
 
+    // The init thread is initially not running.
+    _init_not_running.SetEvent();
 }
 
 // CP3DActiveXCtrl::~CP3DActiveXCtrl - Destructor
@@ -186,20 +189,97 @@ void CP3DActiveXCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInv
     if (!pdc)
         return;
 
-    if ( !m_instance.IsInit( ) )
-    {
-        Init( );
+    switch (_state) {
+    case S_init:
+      {
+        _state = S_loading;
+        // The first time we get the Draw message, we know we're
+        // sufficiently set up to start downloading the instance.  
+        m_instance.read_tokens();
+        get_twirl_bitmaps();
+
+        // Start the twirl timer going.
+        SetTimer(1, 100, timer_callback);
+
+        // But do most of the setup in a child thread, so we don't lock
+        // up the browser GUI while the instance gets itself downloaded
+        // and such.
+        _init_not_running.ResetEvent();  // Now the init thread is running.
+        if (_beginthread(st_init, 0, this) == -1L) {
+          nout << "Couldn't start thread.\n";
+          _init_not_running.SetEvent();
+          _state = S_failed;
+        }
+      }
+      break;
+
+    case S_loading:
+      // Waiting for the init thread to finish.
+      break;
+
+    case S_ready:
+      // Now the instance has downloaded, so set it going.
+      {
+        _state = S_started;
+        KillTimer(1);
+        m_instance.Start( m_instance.GetP3DFilename( ) );
+      }
+      break;
+
+    case S_started:
+      // The instance is running, no need to draw anything.
+      KillTimer(1);
+      DoSuperclassPaint(pdc, rcBounds);
+      return;
+
+    case S_failed:
+      // Something went wrong.
+      KillTimer(1);
+      DoSuperclassPaint(pdc, rcBounds);
+      return;
     }
 
-    CBrush brBackGnd(TranslateColor(AmbientBackColor()));
+    // The instance is setting itself up.  In the meantime, draw the
+    // background and the twirling icon.
+
+    // Paint the background.
+    CBrush brBackGnd(RGB(m_instance._bgcolor_r, m_instance._bgcolor_g, m_instance._bgcolor_b));
     pdc->FillRect(rcBounds, &brBackGnd);
 
-    DoSuperclassPaint(pdc, rcBounds);
+    // Create an in-memory DC compatible with the display DC we're
+    // using to paint
+    CDC dcMemory;
+    dcMemory.CreateCompatibleDC(pdc);
+
+    // Select the bitmap into the in-memory DC
+    DWORD now = GetTickCount();
+    int step = (now / 100) % twirl_num_steps;
+    dcMemory.SelectObject(&_twirl_bitmaps[step]);
+
+    // Find a centerpoint for the bitmap in the client area
+    CRect rect;
+    GetClientRect(&rect);
+    int nX = rect.left + (rect.Width() - twirl_width) / 2;
+    int nY = rect.top + (rect.Height() - twirl_height) / 2;
+
+    // Copy the bits from the in-memory DC into the on-screen DC to
+    // actually do the painting. Use the centerpoint we computed for
+    // the target offset.
+    pdc->BitBlt(nX, nY, twirl_width, twirl_height, &dcMemory, 
+                0, 0, SRCCOPY);
 }
 
 void CP3DActiveXCtrl::OnClose( DWORD dwSaveOption )
 {
 	m_instance.Stop();
+
+    // Make sure the init thread has finished.
+    if (_state == S_loading) {
+      nout << "Waiting for thread stop\n" << flush;
+      ::WaitForSingleObject( _init_not_running.m_hObject, INFINITE ); 
+      nout << "Done waiting for thread stop\n" << flush;
+    }
+    
 	COleControl::OnClose( dwSaveOption );
 }
 
@@ -367,7 +447,17 @@ LRESULT CP3DActiveXCtrl::OnPandaNotification(WPARAM wParam, LPARAM lParam)
     return 0;
 }
 
-int CP3DActiveXCtrl::Init( )
+// The static init method.
+void CP3DActiveXCtrl::
+st_init(void *data) {
+  CP3DActiveXCtrl *self = (CP3DActiveXCtrl *)data;
+  self->init();
+  self->_init_not_running.SetEvent();
+}
+
+// The init method.  This is called once at startup, in a child
+// thread.
+int CP3DActiveXCtrl::init( )
 {
     int error( 0 );
     std::string p3dDllFilename;
@@ -379,9 +469,18 @@ int CP3DActiveXCtrl::Init( )
         if ( !error )
         {
             m_pPandaObject = new PPandaObject( this, NULL );
-            m_instance.Start( m_instance.GetP3DFilename( ) );
+            if (_state == S_loading) {
+              // Ready to start.
+              _state = S_ready;
+              return error;
+            }
         }
     }
+
+    if (_state == S_loading) {
+      // Something went wrong.
+      _state = S_failed;
+    }
     return error;
 }
 
@@ -457,7 +556,10 @@ HRESULT CP3DActiveXCtrl::ExchangeProperties( CPropExchange*  pPX )
 
 P3D_object* CP3DActiveXCtrl::GetP3DObject( )
 {
+  if (_state == S_started) {
     return m_instance.m_p3dObject;
+  }
+  return NULL;
 }
 
 IOleClientSite* CP3DActiveXCtrl::GetClientSte( )
@@ -473,3 +575,41 @@ void CP3DActiveXCtrl::OnmainChanged(void)
 
     SetModifiedFlag();
 }
+
+void CP3DActiveXCtrl::
+get_twirl_bitmaps() {
+  static const size_t twirl_size = twirl_width * twirl_height;
+  unsigned char twirl_data[twirl_size * 3];
+  unsigned char new_data[twirl_size * 4];
+
+  for (int step = 0; step < twirl_num_steps; ++step) {
+    get_twirl_data(twirl_data, twirl_size, step,
+                   m_instance._fgcolor_r, m_instance._fgcolor_g, m_instance._fgcolor_b, 
+                   m_instance._bgcolor_r, m_instance._bgcolor_g, m_instance._bgcolor_b);
+
+    // Expand out the RGB channels into RGBA.
+    for (int yi = 0; yi < twirl_height; ++yi) {
+      const unsigned char *sp = twirl_data + yi * twirl_width * 3;
+      unsigned char *dp = new_data + yi * twirl_width * 4;
+      for (int xi = 0; xi < twirl_width; ++xi) {
+        // RGB <= BGR.
+        dp[0] = sp[2];
+        dp[1] = sp[1];
+        dp[2] = sp[0];
+        dp[3] = (unsigned char)0xff;
+        sp += 3;
+        dp += 4;
+      }
+    }
+
+    // Now load the image.
+    _twirl_bitmaps[step].CreateBitmap(twirl_width, twirl_height, 1, 32, new_data);
+  }
+}
+
+void CP3DActiveXCtrl::
+timer_callback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) {
+  // Just invalidate the region and make it draw again.
+  ::InvalidateRect(hwnd, NULL, FALSE);
+}
+

+ 21 - 3
direct/src/plugin_activex/P3DActiveXCtrl.h

@@ -18,6 +18,7 @@
 #include "PPInstance.h"
 #include "PPPandaObject.h"
 #include "PPInterface.h"
+#include "get_twirl_data.h"
 #include "Mshtml.h"
 
 #include <vector>
@@ -28,7 +29,7 @@ class CP3DActiveXCtrl : public COleControl,
                         public PPInterface
 {
     DECLARE_DYNCREATE(CP3DActiveXCtrl)
-
+    
 // Constructor
 public:
     CP3DActiveXCtrl();
@@ -75,7 +76,8 @@ public:
     };
     afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 
-    int Init( );
+    static void st_init(void *data);
+    int init();
     virtual P3D_object* GetP3DObject( );
     virtual IOleClientSite* GetClientSte();
 
@@ -88,11 +90,27 @@ protected:
     HRESULT ExchangeProperties( CPropExchange* pPropBag );
 
     LRESULT OnPandaNotification(WPARAM wParam, LPARAM lParam);
+    void OnmainChanged(void);
+
+    void get_twirl_bitmaps();
+    static void CALLBACK timer_callback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time);
 
     PPandaObject* m_pPandaObject;
 
     CComPtr<IOleClientSite> m_spClientSite;
 
-    void OnmainChanged(void);
+    CBitmap _twirl_bitmaps[twirl_num_steps];
+
+    enum State {
+      S_init,     // before starting the download
+      S_loading,  // the instance is downloading
+      // From S_loading, only the "init" thread may change the state.
+
+      S_ready,    // the instance is ready to run
+      S_started,  // the instance has successfully started
+      S_failed,   // something went wrong
+    };
+    State _state;
+    CEvent _init_not_running;  // set when the init thread has finished, or before it has started.
 };
 

+ 118 - 57
direct/src/plugin_activex/PPInstance.cpp

@@ -39,6 +39,7 @@
 #include "load_plugin.h"
 #include "find_root_dir.h"
 #include "mkdir_complete.h"
+#include "parse_color.h"
 
 // We can include this header file to get the DTOOL_PLATFORM
 // definition, even though we don't link with dtool.
@@ -86,15 +87,70 @@ PPInstance::PPInstance( CP3DActiveXCtrl& parentCtrl ) :
   _contents_expiration = 0;
   _failed = false;
 
+  _tokens = NULL;
+  _num_tokens = 0;
+
   // Ensure this event is initially in the "set" state, in case we
   // never get a download request before we get a close request.
   m_eventDownloadStopped.SetEvent( );
+  m_eventStop.ResetEvent();
 
   nout << "Plugin is built with " << PANDA_PACKAGE_HOST_URL << "\n";
 }
 
-PPInstance::~PPInstance(  )
-{
+PPInstance::~PPInstance() {
+  assert(_tokens == NULL);
+}
+
+// This is called at setup time to read the set of web tokens from the
+// ActiveX control.
+void PPInstance::
+read_tokens() {
+    assert(_tokens == NULL);
+    _num_tokens = (int)m_parentCtrl.m_parameters.size();
+    _tokens = new P3D_token[ _num_tokens ];
+    for (int i = 0; i < _num_tokens; i++ ) {
+      std::pair< CString, CString > keyAndValue = m_parentCtrl.m_parameters[ i ];
+      // Make the token lowercase, since HTML is case-insensitive but
+      // we're not.
+      string keyword;
+      for (const char *p = m_parentCtrl.m_parameters[ i ].first; *p; ++p) {
+        keyword += tolower(*p);
+      }
+      
+      _tokens[i]._keyword = strdup( keyword.c_str() ); 
+      _tokens[i]._value = strdup( m_parentCtrl.m_parameters[ i ].second );
+    }
+    
+    // fgcolor and bgcolor are useful to know here (in case we have to
+    // draw a twirling icon).
+    
+    // The default bgcolor is white.
+    _bgcolor_r = _bgcolor_g = _bgcolor_b = 0xff;
+    if (has_token("bgcolor")) {
+      int r, g, b;
+      if (parse_color(r, g, b, lookup_token("bgcolor"))) {
+        _bgcolor_r = r;
+        _bgcolor_g = g;
+        _bgcolor_b = b;
+      }
+    }
+    
+    // The default fgcolor is either black or white, according to the
+    // brightness of the bgcolor.
+    if (_bgcolor_r + _bgcolor_g + _bgcolor_b > 0x80 + 0x80 + 0x80) {
+      _fgcolor_r = _fgcolor_g = _fgcolor_b = 0x00;
+    } else {
+      _fgcolor_r = _fgcolor_g = _fgcolor_b = 0xff;
+    }
+    if (has_token("fgcolor")) {
+      int r, g, b;
+      if (parse_color(r, g, b, lookup_token("fgcolor"))) {
+        _fgcolor_r = r;
+        _fgcolor_g = g;
+        _fgcolor_b = b;
+      }
+    }
 }
 
 int PPInstance::DownloadFile( const std::string& from, const std::string& to )
@@ -499,6 +555,8 @@ int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename )
 
 int PPInstance::LoadPlugin( const std::string& dllFilename ) 
 {
+    CSingleLock lock(&_load_mutex);
+    lock.Lock();
     if ( !m_pluginLoaded )
     { 
         ref_plugin();
@@ -558,6 +616,9 @@ int PPInstance::LoadPlugin( const std::string& dllFilename )
 
 int PPInstance::UnloadPlugin()
 {
+    CSingleLock lock(&_load_mutex);
+    lock.Lock();
+
     int error( 0 );
 
     if ( m_pluginLoaded )
@@ -586,7 +647,7 @@ unref_plugin() {
   
   if ( s_instanceCount == 0 && is_plugin_loaded() ) {
     nout << "Unloading core API\n";
-    unload_plugin();
+    unload_plugin(nout);
     
     // This pointer is no longer valid and must be reset for next
     // time.
@@ -596,7 +657,13 @@ unref_plugin() {
 
 int PPInstance::Start( const std::string& p3dFilename  )
 {
-    m_eventStop.ResetEvent();
+    {
+      CSingleLock lock(&_load_mutex);
+      lock.Lock();
+      
+      assert(!m_isInit);
+      m_isInit = true;
+    }
 
     P3D_window_handle parent_window;
     memset(&parent_window, 0, sizeof(parent_window));
@@ -606,29 +673,8 @@ int PPInstance::Start( const std::string& p3dFilename  )
     RECT rect;
     GetClientRect( m_parentCtrl.m_hWnd, &rect );
 
-    P3D_token* p3dTokens = new P3D_token[ m_parentCtrl.m_parameters.size() ];
-    for ( UINT i = 0; i < m_parentCtrl.m_parameters.size(); i++ )
-    {
-        std::pair< CString, CString > keyAndValue = m_parentCtrl.m_parameters[ i ];
-        // Make the token lowercase, since HTML is case-insensitive but
-        // we're not.
-        string keyword;
-        for (const char *p = m_parentCtrl.m_parameters[ i ].first; *p; ++p) {
-          keyword += tolower(*p);
-        }
-
-        p3dTokens[i]._keyword = strdup( keyword.c_str() ); 
-        p3dTokens[i]._value = strdup( m_parentCtrl.m_parameters[ i ].second );
-    }
-    nout << "Creating new P3D instance object \n";
-    m_p3dInstance = P3D_new_instance_ptr( &P3D_NotificationSync, p3dTokens, m_parentCtrl.m_parameters.size(), 0, NULL, (void*)&m_parentCtrl );
-
-    for ( UINT j = 0; j < m_parentCtrl.m_parameters.size(); j++ )
-    {
-        delete [] p3dTokens[j]._keyword;
-        delete [] p3dTokens[j]._value;
-    }
-    delete [] p3dTokens;
+    nout << "Creating new P3D instance object for " << p3dFilename << "\n";
+    m_p3dInstance = P3D_new_instance_ptr( &P3D_NotificationSync, _tokens, _num_tokens, 0, NULL, (void*)&m_parentCtrl );
 
     if ( !m_p3dInstance )
     {
@@ -646,15 +692,12 @@ int PPInstance::Start( const std::string& p3dFilename  )
     P3D_instance_setup_window_ptr( m_p3dInstance, P3D_WT_embedded, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, &parent_window );
 
     nout << "Starting new P3D instance " << p3dFilename << "\n";
-
     if ( !P3D_instance_start_ptr( m_p3dInstance, false, p3dFilename.c_str(), 0 ) )
     {
         nout << "Error starting P3D instance: " << GetLastError() << "\n";
         return 1;
     }
 
-    m_isInit = true;
-
     return 0;
 }
 
@@ -676,6 +719,17 @@ int PPInstance::Stop( )
     {
         UnloadPlugin();
     }
+
+    if (_tokens != NULL) {
+      for ( int j = 0; j < _num_tokens; ++j) {
+        delete [] _tokens[j]._keyword;
+        delete [] _tokens[j]._value;
+      }
+      delete [] _tokens;
+      _tokens = NULL;
+      _num_tokens = 0;
+    }
+
 	return 0;
 }
 
@@ -839,33 +893,6 @@ HandleRequest( P3D_request *request ) {
   return continue_loop;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PPInstance::lookup_token
-//       Access: Private
-//  Description: Returns the value associated with the first
-//               appearance of the named token, or empty string if the
-//               token does not appear.
-////////////////////////////////////////////////////////////////////
-string PPInstance::
-lookup_token(const string &keyword) const {
-  for (UINT i = 0; i < m_parentCtrl.m_parameters.size(); i++) {
-    std::pair<CString, CString> keyAndValue = m_parentCtrl.m_parameters[i];
-    // Make the token lowercase, since HTML is case-insensitive but
-    // we're not.
-    const CString &orig_keyword = m_parentCtrl.m_parameters[i].first;
-    string lower_keyword;
-    for (const char *p = orig_keyword; *p; ++p) {
-      lower_keyword += tolower(*p);
-    }
-    
-    if (lower_keyword == keyword) {
-      return (const char *)m_parentCtrl.m_parameters[i].second;
-    }
-  }
-
-  return string();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::compare_seq
 //       Access: Private, Static
@@ -963,4 +990,38 @@ set_failed() {
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::lookup_token
+//       Access: Private
+//  Description: Returns the value associated with the first
+//               appearance of the named token, or empty string if the
+//               token does not appear.
+////////////////////////////////////////////////////////////////////
+std::string PPInstance::
+lookup_token(const std::string &keyword) const {
+  for (int i = 0; i < _num_tokens; ++i) {
+    if (strcmp(_tokens[i]._keyword, keyword.c_str()) == 0) {
+      return _tokens[i]._value;
+    }
+  }
+
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::has_token
+//       Access: Private
+//  Description: Returns true if the named token appears in the list,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool PPInstance::
+has_token(const std::string &keyword) const {
+  for (int i = 0; i < _num_tokens; ++i) {
+    if (strcmp(_tokens[i]._keyword, keyword.c_str()) == 0) {
+      return true;
+    }
+  }
+
+  return false;
+}
 

+ 12 - 1
direct/src/plugin_activex/PPInstance.h

@@ -37,6 +37,7 @@ public:
     PPInstance( CP3DActiveXCtrl& parentCtrl );
     virtual ~PPInstance( );
 
+    void read_tokens();
     int DownloadP3DComponents( std::string& p3dDllFilename );
 
     int LoadPlugin( const std::string& dllFilename );
@@ -61,6 +62,10 @@ public:
 
     P3D_object* m_p3dObject;
 
+    // Set from fgcolor & bgcolor.
+    int _fgcolor_r, _fgcolor_b, _fgcolor_g;
+    int _bgcolor_r, _bgcolor_b, _bgcolor_g;
+
 protected:
     PPInstance( );
     PPInstance( const PPInstance& );
@@ -77,18 +82,21 @@ protected:
     bool HandleRequest( P3D_request *request );
     static void HandleRequestGetUrl( void *data );
 
-    string lookup_token(const string &keyword) const;
     static int compare_seq(const string &seq_a, const string &seq_b);
     static int compare_seq_int(const char *&num_a, const char *&num_b);
 
     void set_failed();
 
+    std::string lookup_token(const std::string &keyword) const;
+    bool has_token(const std::string &keyword) const;
+
     P3D_instance* m_p3dInstance;
     CP3DActiveXCtrl& m_parentCtrl;
     PPLogger m_logger;
 
     bool m_isInit;
     bool m_pluginLoaded;
+    CMutex _load_mutex;
 
     std::string _download_url_prefix;
     typedef std::vector<std::string> Mirrors;
@@ -98,6 +106,9 @@ protected:
     time_t _contents_expiration;
     bool _failed;
 
+    P3D_token *_tokens;
+    int _num_tokens;
+
     std::string m_rootDir;
 
     class ThreadedRequestData {

+ 2 - 2
direct/src/plugin_npapi/ppInstance.cxx

@@ -100,7 +100,7 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode,
   // fgcolor and bgcolor are useful to know here (in case we have to
   // draw a twirling icon).
 
-  // The default fgcolor is white.
+  // The default bgcolor is white.
   _bgcolor_r = _bgcolor_g = _bgcolor_b = 0xff;
   if (has_token("bgcolor")) {
     int r, g, b;
@@ -1975,7 +1975,7 @@ lookup_token(const string &keyword) const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::has_token
-//       Access: Public
+//       Access: Private
 //  Description: Returns true if the named token appears in the list,
 //               false otherwise.
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
direct/src/plugin_npapi/startup.cxx

@@ -269,7 +269,7 @@ NP_GetEntryPoints(NPPluginFuncs *pluginFuncs) {
 NPError OSCALL
 NP_Shutdown(void) {
   nout << "shutdown\n";
-  unload_plugin();
+  unload_plugin(nout);
   PPBrowserObject::clear_class_definition();
 
   // Not clear whether there's a return value or not.  Some versions

+ 1 - 1
direct/src/plugin_standalone/p3dEmbed.cxx

@@ -241,6 +241,6 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   run_main_loop();
 
 
-  unload_plugin();
+  unload_plugin(cerr);
   return 0;
 }

+ 1 - 1
direct/src/plugin_standalone/panda3d.cxx

@@ -285,7 +285,7 @@ run_command_line(int argc, char *argv[]) {
   run_main_loop();
 
   // All instances have finished; we can exit.
-  unload_plugin();
+  unload_plugin(cerr);
   return 0;
 }