twirling icon for ActiveX too

This commit is contained in:
David Rose 2011-08-26 21:39:40 +00:00
parent 922bb6f48a
commit 1d60925220
10 changed files with 305 additions and 75 deletions

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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 {

View File

@ -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.
////////////////////////////////////////////////////////////////////

View File

@ -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

View File

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

View File

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