open_toontown_panda3d/direct/src/plugin_npapi/ppInstance.cxx

438 lines
13 KiB
C++

// Filename: ppInstance.cxx
// Created by: drose (19Jun09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "ppInstance.h"
#include "startup.h"
#include "p3d_plugin_config.h"
////////////////////////////////////////////////////////////////////
// Function: PPInstance::Constructor
// Access: Public
// Description: Creates a new instance of a Panda3D plugin window.
// The create_data structure is supplied from NPAPI, and
// defines the initial parameters specified in the HTML
// document.
////////////////////////////////////////////////////////////////////
PPInstance::
PPInstance(NPMIMEType pluginType, NPP instance, uint16 mode,
int16 argc, char *argn[], char *argv[], NPSavedData *saved) {
logfile << "constructing " << this << "\n" << flush;
_p3d_inst = NULL;
_npp_instance = instance;
_npp_mode = mode;
// Copy the tokens and save them within this object.
_tokens.reserve(argc);
for (int i = 0; i < argc; ++i) {
P3D_token token;
token._keyword = strdup(argn[i]);
token._value = strdup(argv[i]);
logfile
<< " " << i << ": " << token._keyword << " = " << token._value << "\n";
_tokens.push_back(token);
}
_started_instance_data = false;
_got_instance_data = false;
_got_window = false;
if (!is_plugin_loaded()) {
// Start the plugin DLL downloading.
string url = P3D_PLUGIN_DOWNLOAD;
url += P3D_PLUGIN_PLATFORM;
url += "/";
url += get_plugin_basename();
PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
browser->geturlnotify(_npp_instance, url.c_str(), NULL, req);
}
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PPInstance::
~PPInstance() {
logfile
<< "destructing " << this << "\n" << flush;
if (_p3d_inst != NULL) {
P3D_instance_finish(_p3d_inst);
_p3d_inst = NULL;
}
// Free the tokens we allocated.
Tokens::iterator ti;
for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
free((char *)(*ti)._keyword);
free((char *)(*ti)._value);
}
_tokens.clear();
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::set_window
// Access: Public
// Description: Stores or updates the window parameters.
////////////////////////////////////////////////////////////////////
void PPInstance::
set_window(NPWindow *window) {
if (window->x == _window.x &&
window->y == _window.y &&
window->width == _window.width &&
window->height == _window.height) {
// No changes.
return;
}
_window = *window;
_got_window = true;
if (_p3d_inst == NULL) {
create_instance();
} else {
send_window();
}
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::new_stream
// Access: Public
// Description: Receives notification of a new stream object, e.g. a
// url request.
////////////////////////////////////////////////////////////////////
NPError PPInstance::
new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) {
if (stream->notifyData == NULL) {
// This is an unsolicited stream. Assume the first unsolicited
// stream we receive is the instance data; any other unsolicited
// stream is an error.
if (!_started_instance_data) {
stream->notifyData = new PPDownloadRequest(PPDownloadRequest::RT_instance_data);
*stype = NP_ASFILEONLY;
return NPERR_NO_ERROR;
}
// This is an unexpected unsolicited stream. (Firefox seems to
// give us the instance data twice for some reason.)
return NPERR_GENERIC_ERROR;
}
PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
switch (req->_rtype) {
case PPDownloadRequest::RT_core_dll:
// This is the core API DLL (or dylib or whatever). We want to
// download this to file so we can run it directly.
*stype = NP_ASFILEONLY;
return NPERR_NO_ERROR;
case PPDownloadRequest::RT_user:
// This is a request from the plugin. We'll receive this as a
// stream.
*stype = NP_NORMAL;
return NPERR_NO_ERROR;
default:
// Don't know what this is.
logfile << "Unexpected request " << (int)req->_rtype << "\n";
}
return NPERR_GENERIC_ERROR;
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::write_stream
// Access: Public
// Description: Called by the browser to feed data read from a URL or
// whatever.
////////////////////////////////////////////////////////////////////
int PPInstance::
write_stream(NPStream *stream, int offset, int len, void *buffer) {
if (stream->notifyData == NULL) {
logfile << "Unexpected write_stream on " << stream->url << "\n";
return 0;
}
PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
switch (req->_rtype) {
case PPDownloadRequest::RT_user:
P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
P3D_RC_in_progress, 0,
stream->end, buffer, len);
return len;
default:
logfile << "Unexpected write_stream on " << stream->url << "\n";
break;
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::destroy_stream
// Access: Public
// Description: Called by the browser to mark the end of a stream;
// the file has either been successfully downloaded or
// failed.
////////////////////////////////////////////////////////////////////
NPError PPInstance::
destroy_stream(NPStream *stream, NPReason reason) {
if (stream->notifyData == NULL) {
logfile << "Unexpected destroy_stream on " << stream->url << "\n";
return NPERR_GENERIC_ERROR;
}
PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
switch (req->_rtype) {
case PPDownloadRequest::RT_user:
{
P3D_result_code result_code = P3D_RC_done;
if (reason != NPRES_DONE) {
result_code = P3D_RC_generic_error;
}
assert(!req->_notified_done);
P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
result_code, 0, stream->end, NULL, 0);
req->_notified_done = true;
}
break;
default:
break;
}
return NPERR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::url_notify
// Access: Public
// Description: Called by the browser to announce the end of a
// stream. This normally follows destroy_stream(),
// unless the stream was never created in the first
// place.
////////////////////////////////////////////////////////////////////
void PPInstance::
url_notify(const char *url, NPReason reason, void *notifyData) {
if (notifyData == NULL) {
return;
}
PPDownloadRequest *req = (PPDownloadRequest *)notifyData;
switch (req->_rtype) {
case PPDownloadRequest::RT_user:
if (!req->_notified_done) {
// We shouldn't have gotten here without notifying the stream
// unless the stream never got started (and hence we never
// called destroy_stream().
logfile << "Failure starting stream\n" << flush;
assert(reason != NPRES_DONE);
P3D_instance_feed_url_stream(_p3d_inst, req->_user_id,
P3D_RC_generic_error, 0, 0, NULL, 0);
req->_notified_done = true;
}
break;
default:
break;
}
// delete req;
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::stream_as_file
// Access: Public
// Description: Called by the browser to report the filename that
// contains the fully-downloaded stream contents.
////////////////////////////////////////////////////////////////////
void PPInstance::
stream_as_file(NPStream *stream, const char *fname) {
if (stream->notifyData == NULL) {
logfile << "Unexpected stream_as_file on " << stream->url << "\n";
return;
}
string filename = fname;
#ifdef __APPLE__
// Safari seems to want to report the filename in the old-style form
// "Macintosh HD:blah:blah:blah" instead of the new-style form
// "/blah/blah/blah". How annoying.
// TODO: Is "Macintosh HD:" the only possible prefix?
if (filename.substr(0, 13) == "Macintosh HD:") {
string fname2;
for (size_t p = 12; p < filename.size(); ++p) {
if (filename[p] == ':') {
fname2 += '/';
} else {
fname2 += filename[p];
}
}
filename = fname2;
logfile << "converted filename to " << filename << "\n";
}
#endif // __APPLE__
PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
switch (req->_rtype) {
case PPDownloadRequest::RT_core_dll:
{
// This is the core API DLL (or dylib or whatever). Now that
// we've downloaded it, we can load it.
logfile << "got plugin " << filename << "\n" << flush;
string override_filename = P3D_PLUGIN_P3D_PLUGIN;
if (!override_filename.empty()) {
filename = override_filename;
}
if (!load_plugin(filename)) {
logfile << "Unable to launch core API.\n";
break;
}
logfile << "loaded core API " << filename << "\n" << flush;
create_instance();
}
break;
case PPDownloadRequest::RT_instance_data:
// This is the instance data, e.g. the p3d filename. Now we can
// launch the instance.
_got_instance_data = true;
_p3d_filename = filename;
create_instance();
break;
default:
// Don't know what this is.
logfile << "Unexpected stream_as_file, type " << (int)req->_rtype << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::handle_request
// Access: Public
// Description: Handles a request from the plugin, forwarding
// it to the browser as appropriate.
////////////////////////////////////////////////////////////////////
void PPInstance::
handle_request(P3D_request *request) {
logfile << "handle_request: " << request << "\n";
assert(request->_instance == _p3d_inst);
bool handled = false;
switch (request->_request_type) {
case P3D_RT_stop:
logfile << "Got P3D_RT_stop\n";
if (_p3d_inst != NULL) {
P3D_instance_finish(_p3d_inst);
_p3d_inst = NULL;
}
// Guess the browser doesn't really care.
handled = true;
break;
case P3D_RT_get_url:
{
logfile << "Got P3D_RT_get_url: " << request->_request._get_url._url
<< "\n";
PPDownloadRequest *req =
new PPDownloadRequest(PPDownloadRequest::RT_user,
request->_request._get_url._unique_id);
browser->geturlnotify(_npp_instance, request->_request._get_url._url,
NULL, req);
}
break;
default:
// Some request types are not handled.
logfile << "Unhandled request: " << request->_request_type << "\n";
break;
};
P3D_request_finish(request, handled);
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::create_instance
// Access: Private
// Description: Actually creates the internal P3D_instance object, if
// possible and needed.
////////////////////////////////////////////////////////////////////
void PPInstance::
create_instance() {
if (_p3d_inst != NULL) {
// Already created.
return;
}
if (!is_plugin_loaded()) {
// Plugin is not loaded yet.
return;
}
if (!_got_instance_data) {
// No instance data yet.
return;
}
if (!_got_window) {
// No window yet.
return;
}
const P3D_token *tokens = NULL;
if (!_tokens.empty()) {
tokens = &_tokens[0];
}
_p3d_inst = P3D_create_instance
(request_ready, this, _p3d_filename.c_str(), tokens, _tokens.size());
if (_p3d_inst != NULL) {
send_window();
}
}
////////////////////////////////////////////////////////////////////
// Function: PPInstance::send_window
// Access: Private
// Description: Actually issues the window parameters to the internal
// P3D_instance object.
////////////////////////////////////////////////////////////////////
void PPInstance::
send_window() {
assert(_p3d_inst != NULL);
P3D_window_handle parent_window;
#ifdef _WIN32
parent_window._hwnd = (HWND)(_window.window);
#endif
P3D_instance_setup_window
(_p3d_inst, P3D_WT_toplevel,
_window.x, _window.y, _window.width, _window.height,
parent_window);
}