589 lines
18 KiB
C++
589 lines
18 KiB
C++
/**
|
|
* 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."
|
|
*
|
|
* @file startup.cxx
|
|
* @author drose
|
|
* @date 2009-06-17
|
|
*/
|
|
|
|
#include "startup.h"
|
|
#include "p3d_plugin_config.h"
|
|
#include "p3d_lock.h"
|
|
#include "ppBrowserObject.h"
|
|
#include "wstring_encode.h"
|
|
#include "find_root_dir.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <shlobj.h>
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
static ofstream logfile;
|
|
ostream *nout_stream = &logfile;
|
|
|
|
string global_root_dir;
|
|
bool has_plugin_thread_async_call;
|
|
|
|
NPNetscapeFuncs *browser;
|
|
|
|
// These function prototypes changed slightly (but insignificantly) between
|
|
// releases of NPAPI. To avoid compilation errors, we use our own name, and
|
|
// cast it to the correct type.
|
|
static int32_t
|
|
NPP_WriteReady_x(NPP instance, NPStream *stream);
|
|
static int32_t
|
|
NPP_Write_x(NPP instance, NPStream *stream, int32_t offset,
|
|
int32_t len, void *buffer);
|
|
|
|
// open_logfile() also assigns global_root_dir.
|
|
static bool logfile_is_open = false;
|
|
static void
|
|
open_logfile() {
|
|
if (!logfile_is_open) {
|
|
global_root_dir = find_root_dir();
|
|
|
|
// Note that this logfile name may not be specified at runtime. It must
|
|
// be compiled in if it is specified at all.
|
|
|
|
string log_directory;
|
|
// Allow the developer to compile in the log directory.
|
|
#ifdef P3D_PLUGIN_LOG_DIRECTORY
|
|
if (log_directory.empty()) {
|
|
log_directory = P3D_PLUGIN_LOG_DIRECTORY;
|
|
}
|
|
#endif
|
|
|
|
// Failing that, we write logfiles to Panda3Dlog.
|
|
if (log_directory.empty()) {
|
|
log_directory = global_root_dir + "/log";
|
|
}
|
|
mkdir_complete(log_directory, cerr);
|
|
|
|
// Ensure that the log directory ends with a slash.
|
|
if (!log_directory.empty() && log_directory[log_directory.size() - 1] != '/') {
|
|
#ifdef _WIN32
|
|
if (log_directory[log_directory.size() - 1] != '\\')
|
|
#endif
|
|
log_directory += "/";
|
|
}
|
|
|
|
// Construct the logfile pathname.
|
|
|
|
string log_basename;
|
|
#ifdef P3D_PLUGIN_LOG_BASENAME1
|
|
if (log_basename.empty()) {
|
|
log_basename = P3D_PLUGIN_LOG_BASENAME1;
|
|
}
|
|
#endif
|
|
if (log_basename.empty()) {
|
|
log_basename = "p3dplugin";
|
|
}
|
|
|
|
if (!log_basename.empty()) {
|
|
string log_pathname = log_directory;
|
|
log_pathname += log_basename;
|
|
log_pathname += ".log";
|
|
|
|
logfile.close();
|
|
logfile.clear();
|
|
#ifdef _WIN32
|
|
wstring log_pathname_w;
|
|
string_to_wstring(log_pathname_w, log_pathname);
|
|
logfile.open(log_pathname_w.c_str(), ios::out | ios::trunc);
|
|
#else
|
|
logfile.open(log_pathname.c_str(), ios::out | ios::trunc);
|
|
#endif // _WIN32
|
|
logfile.setf(ios::unitbuf);
|
|
}
|
|
|
|
// If we didn't have a logfile name compiled in, we throw away log output
|
|
// by the simple expedient of never actually opening the ofstream.
|
|
logfile_is_open = true;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* On Unix, this function is called by the browser to get the mimetypes and
|
|
* extensions this plugin is supposed to handle.
|
|
*/
|
|
#if NP_VERSION_MAJOR == 0 && NP_VERSION_MINOR <= 22
|
|
char *
|
|
#else
|
|
const char *
|
|
#endif
|
|
NP_GetMIMEDescription(void) {
|
|
return "application/x-panda3d:p3d:Panda3D applet;";
|
|
}
|
|
|
|
/**
|
|
* On Unix, this function is called by the browser to get some information
|
|
* like the name and description.
|
|
*/
|
|
NPError
|
|
NP_GetValue(void*, NPPVariable variable, void* value) {
|
|
if (value == NULL) {
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
switch (variable) {
|
|
case NPPVpluginNameString:
|
|
*(const char **)value = "Panda3D Game Engine Plug-In";
|
|
break;
|
|
case NPPVpluginDescriptionString:
|
|
*(const char **)value = "Runs 3-D games and interactive applets";
|
|
break;
|
|
default:
|
|
nout << "Ignoring GetValue request " << variable << "\n";
|
|
return NPERR_INVALID_PARAM;
|
|
}
|
|
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* This function is called (almost) before any other function, to ask the
|
|
* plugin to initialize itself and to send the pointers to the browser control
|
|
* functions. Also see NP_GetEntryPoints.
|
|
*/
|
|
#ifdef _WIN32
|
|
NPError OSCALL
|
|
NP_Initialize(NPNetscapeFuncs *browserFuncs)
|
|
#else
|
|
// On Mac, the API specifies this second parameter is included, but it lies.
|
|
// We actually don't get a second parameter there, but we have to put it here
|
|
// to make the compiler happy.
|
|
NPError OSCALL
|
|
NP_Initialize(NPNetscapeFuncs *browserFuncs,
|
|
NPPluginFuncs *pluginFuncs)
|
|
#endif
|
|
{
|
|
// save away browser functions
|
|
browser = browserFuncs;
|
|
|
|
// open_logfile() also assigns global_root_dir.
|
|
open_logfile();
|
|
nout << "Initializing Panda3D plugin version " << P3D_PLUGIN_VERSION_STR << "\n";
|
|
|
|
nout << "browserFuncs = " << browserFuncs << "\n";
|
|
|
|
// On Unix, we have to use the pluginFuncs argument to pass our entry
|
|
// points.
|
|
#if !defined(_WIN32) && !defined(__APPLE__)
|
|
if (pluginFuncs != NULL) {
|
|
NP_GetEntryPoints(pluginFuncs);
|
|
}
|
|
#endif
|
|
|
|
int browser_major = (browser->version >> 8) & 0xff;
|
|
int browser_minor = browser->version & 0xff;
|
|
nout << "Browser NPAPI version " << browser_major << "." << browser_minor << "\n";
|
|
|
|
int expected_major = NP_VERSION_MAJOR;
|
|
int expected_minor = NP_VERSION_MINOR;
|
|
|
|
nout << "Plugin compiled with NPAPI version "
|
|
<< expected_major << "." << expected_minor << "\n";
|
|
|
|
has_plugin_thread_async_call = false;
|
|
#ifdef HAS_PLUGIN_THREAD_ASYNC_CALL
|
|
// Check if the browser offers this very useful call.
|
|
if (browser_major > 0 || browser_minor >= NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL) {
|
|
if ((void *)browser->pluginthreadasynccall == (void *)NULL) {
|
|
nout << "Browser should have PLUGIN_THREAD_ASYNC_CALL, but the pointer is NULL.\n";
|
|
has_plugin_thread_async_call = false;
|
|
} else {
|
|
has_plugin_thread_async_call = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Seed the lame random number generator in rand(); we use it to select a
|
|
// mirror for downloading.
|
|
srand((unsigned int)time(NULL));
|
|
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* This method is extracted directly from the DLL and called at initialization
|
|
* time by the browser, either before or after NP_Initialize, to retrieve the
|
|
* pointers to the rest of the plugin functions that are not exported from the
|
|
* DLL.
|
|
*/
|
|
NPError OSCALL
|
|
NP_GetEntryPoints(NPPluginFuncs *pluginFuncs) {
|
|
// open_logfile() also assigns global_root_dir.
|
|
open_logfile();
|
|
nout << "NP_GetEntryPoints, pluginFuncs = " << pluginFuncs << "\n";
|
|
if (pluginFuncs->size == 0) {
|
|
pluginFuncs->size = sizeof(*pluginFuncs);
|
|
}
|
|
if (pluginFuncs->size < offsetof(NPPluginFuncs, gotfocus)) {
|
|
nout << "Invalid NPPPluginFuncs size\n";
|
|
return NPERR_INVALID_FUNCTABLE_ERROR;
|
|
}
|
|
|
|
// Not entirely sure what version number we should send back here. Sending
|
|
// the verion number of the NPAPI library we're compiled against doesn't
|
|
// seem 100% right, because there's no reason to think that *this* code
|
|
// knows about all of the functions provided by the particular library
|
|
// version it's compiled against.
|
|
pluginFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
|
|
|
|
pluginFuncs->newp = NPP_New;
|
|
pluginFuncs->destroy = NPP_Destroy;
|
|
pluginFuncs->setwindow = NPP_SetWindow;
|
|
pluginFuncs->newstream = NPP_NewStream;
|
|
pluginFuncs->destroystream = NPP_DestroyStream;
|
|
pluginFuncs->asfile = NPP_StreamAsFile;
|
|
|
|
// WebKit's NPAPI defines the wrong prototype for these functions, so we
|
|
// have to cast them. But the casting typename isn't consistent between
|
|
// WebKit and Mozilla's NPAPI headers.
|
|
#ifdef NewNPP_WriteProc
|
|
pluginFuncs->writeready = NewNPP_WriteReadyProc(NPP_WriteReady_x);
|
|
pluginFuncs->write = NewNPP_WriteProc(NPP_Write_x);
|
|
#else
|
|
pluginFuncs->writeready = (NPP_WriteReadyProcPtr)NPP_WriteReady_x;
|
|
pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write_x;
|
|
#endif
|
|
|
|
pluginFuncs->print = NPP_Print;
|
|
pluginFuncs->event = NPP_HandleEvent;
|
|
pluginFuncs->urlnotify = NPP_URLNotify;
|
|
pluginFuncs->getvalue = NPP_GetValue;
|
|
pluginFuncs->setvalue = NPP_SetValue;
|
|
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* This function is called when the browser is done with the plugin; it asks
|
|
* the plugin to unload itself and free all used resources.
|
|
*/
|
|
NPError OSCALL
|
|
NP_Shutdown(void) {
|
|
nout << "shutdown\n";
|
|
unload_plugin(nout);
|
|
PPBrowserObject::clear_class_definition();
|
|
|
|
// Not clear whether there's a return value or not. Some versions of the
|
|
// API have different opinions on this.
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to create a new instance of the plugin.
|
|
*/
|
|
NPError
|
|
NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode,
|
|
int16_t argc, char *argn[], char *argv[], NPSavedData *saved) {
|
|
nout << "new instance " << instance << "\n";
|
|
|
|
P3D_window_handle_type window_handle_type = P3D_WHT_none;
|
|
P3D_event_type event_type = P3D_ET_none;
|
|
|
|
#ifdef __APPLE__
|
|
// The default drawing model for Apple is via the deprecated QuickDraw
|
|
// GrafPtr, and the default event model is via the deprecated Carbon
|
|
// EventRecord.
|
|
window_handle_type = P3D_WHT_osx_port;
|
|
event_type = P3D_ET_osx_event_record;
|
|
|
|
// But we have to request the CoreGraphics drawing model to be compatible
|
|
// with Snow Leopard.
|
|
NPBool supports_core_graphics = false;
|
|
#ifdef MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL
|
|
NPError err = browser->getvalue(instance,
|
|
NPNVsupportsCoreGraphicsBool,
|
|
&supports_core_graphics);
|
|
if (err != NPERR_NO_ERROR) {
|
|
supports_core_graphics = false;
|
|
}
|
|
|
|
if (supports_core_graphics) {
|
|
// Set the drawing model
|
|
err = browser->setvalue(instance,
|
|
(NPPVariable)NPNVpluginDrawingModel,
|
|
(void *)NPDrawingModelCoreGraphics);
|
|
if (err == NPERR_NO_ERROR) {
|
|
window_handle_type = P3D_WHT_osx_cgcontext;
|
|
}
|
|
}
|
|
#endif // MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL
|
|
nout << "supports_core_graphics = " << (bool)supports_core_graphics
|
|
<< " window_handle_type = " << window_handle_type << "\n";
|
|
|
|
// And Snow Leopard also wants the new Cocoa event model.
|
|
NPBool supports_cocoa = false;
|
|
#ifdef MACOSX_HAS_EVENT_MODELS
|
|
err = browser->getvalue(instance, NPNVsupportsCocoaBool, &supports_cocoa);
|
|
if (err != NPERR_NO_ERROR) {
|
|
supports_cocoa = false;
|
|
}
|
|
|
|
if (supports_cocoa) {
|
|
// Set the event model
|
|
err = browser->setvalue(instance,
|
|
(NPPVariable)NPPVpluginEventModel,
|
|
(void *)NPEventModelCocoa);
|
|
if (err == NPERR_NO_ERROR) {
|
|
event_type = P3D_ET_osx_cocoa;
|
|
}
|
|
}
|
|
#endif // MACOSX_HAS_EVENT_MODELS
|
|
nout << "supports_cocoa = " << (bool)supports_cocoa
|
|
<< " event_type = " << event_type << "\n";
|
|
#endif // __APPLE__
|
|
|
|
PPInstance *inst = new PPInstance(pluginType, instance, mode,
|
|
argc, argn, argv, saved,
|
|
window_handle_type, event_type);
|
|
instance->pdata = inst;
|
|
nout << "new instance->pdata = " << inst << "\n";
|
|
|
|
// To experiment with a "windowless" plugin, which really means we create
|
|
// our own window without an intervening window, try this.
|
|
// browser->setvalue(instance, NPPVpluginWindowBool, (void *)false);
|
|
|
|
// Now that we have stored the pointer, we can call begin(), which starts to
|
|
// initiate downloads.
|
|
inst->begin();
|
|
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to destroy an instance of the plugin previously
|
|
* created with NPP_New.
|
|
*/
|
|
NPError
|
|
NPP_Destroy(NPP instance, NPSavedData **save) {
|
|
nout << "destroy instance " << instance << ", "
|
|
<< (PPInstance *)instance->pdata << "\n";
|
|
nout << "save = " << (void *)save << "\n";
|
|
// (*save) = NULL;
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
inst->stop_outstanding_streams();
|
|
|
|
delete inst;
|
|
instance->pdata = NULL;
|
|
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to inform the instance of its window size and
|
|
* placement. This is called initially to create the window, and may be
|
|
* called subsequently when the window needs to be moved. It may be called
|
|
* redundantly.
|
|
*/
|
|
NPError
|
|
NPP_SetWindow(NPP instance, NPWindow *window) {
|
|
nout << "SetWindow " << window->x << ", " << window->y
|
|
<< ", " << window->width << ", " << window->height
|
|
<< "\n";
|
|
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
inst->set_window(window);
|
|
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
/**
|
|
* Called by the browser when a new data stream is created, usually in
|
|
* response to a geturl request; but it is also called initially to supply the
|
|
* data in the data or src element. The plugin must specify how it can
|
|
* receive the stream.
|
|
*/
|
|
NPError
|
|
NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream,
|
|
NPBool seekable, uint16_t *stype) {
|
|
nout << "NewStream " << type << ": " << (void *)stream
|
|
<< ", " << stream->url << ", size = " << stream->end
|
|
<< ", notifyData = " << stream->notifyData
|
|
<< ", for " << instance
|
|
<< ", " << (PPInstance *)(instance->pdata) << "\n";
|
|
PPInstance::generic_browser_call();
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
return inst->new_stream(type, stream, seekable != 0, stype);
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to mark the end of a stream created with NewStream.
|
|
*/
|
|
NPError
|
|
NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) {
|
|
nout << "DestroyStream: " << (void *)stream << ", " << stream->url
|
|
<< ", notifyData = " << stream->notifyData
|
|
<< ", reason = " << reason
|
|
<< ", for " << instance
|
|
<< ", " << (PPInstance *)(instance->pdata) << "\n";
|
|
|
|
PPInstance::generic_browser_call();
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
return inst->destroy_stream(stream, reason);
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to ask how many bytes it can deliver for a stream.
|
|
*/
|
|
int32_t
|
|
NPP_WriteReady_x(NPP instance, NPStream *stream) {
|
|
// nout << "WriteReady " << stream->url << " for " << instance << ", " <<
|
|
// (PPInstance *)(instance->pdata) << "\n";
|
|
PPInstance::generic_browser_call();
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
return inst->write_ready(stream);
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to deliver bytes for the stream; the plugin should
|
|
* return the number of bytes consumed.
|
|
*/
|
|
int32_t
|
|
NPP_Write_x(NPP instance, NPStream *stream, int32_t offset,
|
|
int32_t len, void *buffer) {
|
|
// nout << "Write " << stream->url << ", " << offset << ", " << len << " for
|
|
// " << instance << ", " << (PPInstance *)(instance->pdata) << "\n";
|
|
PPInstance::generic_browser_call();
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
return inst->write_stream(stream, offset, len, buffer);
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to report the filename that contains the fully-
|
|
* downloaded stream, if NP_ASFILEONLY was specified by the plugin in
|
|
* NPP_NewStream.
|
|
*/
|
|
void
|
|
NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname) {
|
|
nout << "StreamAsFile " << stream->url
|
|
<< ", " << stream->end
|
|
<< ", notifyData = " << stream->notifyData
|
|
<< "\n";
|
|
|
|
PPInstance::generic_browser_call();
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
inst->stream_as_file(stream, fname);
|
|
}
|
|
|
|
/**
|
|
* Called by the browser when the user attempts to print the page containing
|
|
* the plugin instance.
|
|
*/
|
|
void
|
|
NPP_Print(NPP instance, NPPrint *platformPrint) {
|
|
nout << "Print\n";
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to inform the plugin of OS window events.
|
|
*/
|
|
int16_t
|
|
NPP_HandleEvent(NPP instance, void *event) {
|
|
// nout << "HandleEvent\n";
|
|
PPInstance::generic_browser_call();
|
|
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
return inst->handle_event(event);
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to inform the plugin of a completed URL request.
|
|
*/
|
|
void
|
|
NPP_URLNotify(NPP instance, const char *url,
|
|
NPReason reason, void *notifyData) {
|
|
nout << "URLNotify: " << url
|
|
<< ", notifyData = " << notifyData
|
|
<< ", reason = " << reason
|
|
<< "\n";
|
|
|
|
PPInstance::generic_browser_call();
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
inst->url_notify(url, reason, notifyData);
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to query specific information from the plugin.
|
|
*/
|
|
NPError
|
|
NPP_GetValue(NPP instance, NPPVariable variable, void *value) {
|
|
nout << "GetValue " << variable << "\n";
|
|
PPInstance::generic_browser_call();
|
|
PPInstance *inst = (PPInstance *)(instance->pdata);
|
|
assert(inst != NULL);
|
|
|
|
if (variable == NPPVpluginScriptableNPObject) {
|
|
NPObject *obj = inst->get_panda_script_object();
|
|
if (obj != NULL) {
|
|
*(NPObject **)value = obj;
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
} else if (variable == NPPVpluginNeedsXEmbed) {
|
|
// If we have Gtk2 available, we can use it to support the XEmbed
|
|
// protocol, which Chromium (at least) requires.
|
|
|
|
// In this case, we'll say we can do it, if the browser supports it.
|
|
// (Though probably the browser wouldn't be asking if it couldn't.)
|
|
|
|
NPBool supports_xembed = false;
|
|
NPError err = browser->getvalue(instance, NPNVSupportsXEmbedBool, &supports_xembed);
|
|
if (err != NPERR_NO_ERROR) {
|
|
supports_xembed = false;
|
|
}
|
|
nout << "browser supports_xembed: " << (supports_xembed != 0) << "\n";
|
|
#ifdef HAVE_GTK
|
|
bool plugin_supports = true;
|
|
#else
|
|
bool plugin_supports = false;
|
|
supports_xembed = false;
|
|
#endif // HAVE_GTK
|
|
nout << "plugin supports_xembed: " << plugin_supports << "\n";
|
|
|
|
inst->set_xembed(supports_xembed != 0);
|
|
*(NPBool *)value = supports_xembed;
|
|
|
|
return NPERR_NO_ERROR;
|
|
|
|
} else {
|
|
return NP_GetValue(NULL, variable, value);
|
|
}
|
|
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
|
|
/**
|
|
* Called by the browser to update a scriptable value.
|
|
*/
|
|
NPError
|
|
NPP_SetValue(NPP instance, NPNVariable variable, void *value) {
|
|
nout << "SetValue " << variable << "\n";
|
|
PPInstance::generic_browser_call();
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|