From 1d60925220af43948ae36480ba02388036ca23fd Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 26 Aug 2011 21:39:40 +0000 Subject: [PATCH] twirling icon for ActiveX too --- direct/src/plugin/load_plugin.cxx | 2 +- direct/src/plugin/load_plugin.h | 2 +- direct/src/plugin_activex/P3DActiveXCtrl.cpp | 154 +++++++++++++++- direct/src/plugin_activex/P3DActiveXCtrl.h | 24 ++- direct/src/plugin_activex/PPInstance.cpp | 175 +++++++++++++------ direct/src/plugin_activex/PPInstance.h | 13 +- direct/src/plugin_npapi/ppInstance.cxx | 4 +- direct/src/plugin_npapi/startup.cxx | 2 +- direct/src/plugin_standalone/p3dEmbed.cxx | 2 +- direct/src/plugin_standalone/panda3d.cxx | 2 +- 10 files changed, 305 insertions(+), 75 deletions(-) diff --git a/direct/src/plugin/load_plugin.cxx b/direct/src/plugin/load_plugin.cxx index 2a3d94444b..04a8ac2370 100755 --- a/direct/src/plugin/load_plugin.cxx +++ b/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; } diff --git a/direct/src/plugin/load_plugin.h b/direct/src/plugin/load_plugin.h index 16b7922676..985eef7261 100755 --- a/direct/src/plugin/load_plugin.h +++ b/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 diff --git a/direct/src/plugin_activex/P3DActiveXCtrl.cpp b/direct/src/plugin_activex/P3DActiveXCtrl.cpp index b460fefcfb..e35199eedd 100644 --- a/direct/src/plugin_activex/P3DActiveXCtrl.cpp +++ b/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); +} + diff --git a/direct/src/plugin_activex/P3DActiveXCtrl.h b/direct/src/plugin_activex/P3DActiveXCtrl.h index 9dadf4a255..c3914e247f 100644 --- a/direct/src/plugin_activex/P3DActiveXCtrl.h +++ b/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 @@ -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 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. }; diff --git a/direct/src/plugin_activex/PPInstance.cpp b/direct/src/plugin_activex/PPInstance.cpp index 423b1343e7..994f82df40 100644 --- a/direct/src/plugin_activex/PPInstance.cpp +++ b/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 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; +} diff --git a/direct/src/plugin_activex/PPInstance.h b/direct/src/plugin_activex/PPInstance.h index 42edb48319..61904e1932 100644 --- a/direct/src/plugin_activex/PPInstance.h +++ b/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 Mirrors; @@ -98,6 +106,9 @@ protected: time_t _contents_expiration; bool _failed; + P3D_token *_tokens; + int _num_tokens; + std::string m_rootDir; class ThreadedRequestData { diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx index 2960458f70..f87ec0e8f2 100644 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ b/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. //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin_npapi/startup.cxx b/direct/src/plugin_npapi/startup.cxx index 4bc1409d05..84496b0617 100644 --- a/direct/src/plugin_npapi/startup.cxx +++ b/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 diff --git a/direct/src/plugin_standalone/p3dEmbed.cxx b/direct/src/plugin_standalone/p3dEmbed.cxx index 26beeb54a5..ac19baacc6 100644 --- a/direct/src/plugin_standalone/p3dEmbed.cxx +++ b/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; } diff --git a/direct/src/plugin_standalone/panda3d.cxx b/direct/src/plugin_standalone/panda3d.cxx index 19a21e16ec..15e7d0d1fb 100644 --- a/direct/src/plugin_standalone/panda3d.cxx +++ b/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; }