godot/platform/nacl/godot_module.cpp
2016-01-01 11:50:53 -02:00

333 lines
9.8 KiB
C++

/*************************************************************************/
/* godot_module.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "opengl_context.h"
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/gles2/gl2ext_ppapi.h"
#include "ppapi/cpp/rect.h"
#include "ppapi/cpp/size.h"
#include "ppapi/cpp/var.h"
#include "geturl_handler.h"
#include "core/variant.h"
#include "os_nacl.h"
extern int nacl_main(int argc, const char** argn, const char** argv);
extern void nacl_cleanup();
static String pkg_url;
pp::Instance* godot_instance = NULL;
struct StateData {
int arg_count;
Array args;
String method;
};
extern OSNacl* os_nacl;
class GodotInstance : public pp::Instance {
enum State {
STATE_METHOD,
STATE_PARAM_COUNT,
STATE_PARAMS,
STATE_CALL,
};
State state;
StateData* sd;
SharedOpenGLContext opengl_context_;
int width;
int height;
#define MAX_ARGS 64
uint32_t init_argc;
char* init_argn[MAX_ARGS];
char* init_argv[MAX_ARGS];
bool package_loaded;
GetURLHandler* package_pending;
public:
explicit GodotInstance(PP_Instance instance) : pp::Instance(instance) {
printf("GodotInstance!\n");
state = STATE_METHOD;
RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD | PP_INPUTEVENT_CLASS_WHEEL | PP_INPUTEVENT_CLASS_TOUCH);
sd = NULL;
package_pending = NULL;
package_loaded = false;
godot_instance = this;
}
virtual ~GodotInstance() {
nacl_cleanup();
}
/// Called by the browser to handle the postMessage() call in Javascript.
/// Detects which method is being called from the message contents, and
/// calls the appropriate function. Posts the result back to the browser
/// asynchronously.
/// @param[in] var_message The message posted by the browser. The possible
/// messages are 'fortyTwo' and 'reverseText:Hello World'. Note that
/// the 'reverseText' form contains the string to reverse following a ':'
/// separator.
virtual void HandleMessage(const pp::Var& var_message);
bool HandleInputEvent(const pp::InputEvent& event);
bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
printf("******* init! %i, %p, %p\n", argc, argn, argv);
fflush(stdout);
if (opengl_context_ == NULL) {
opengl_context_.reset(new OpenGLContext(this));
};
opengl_context_->InvalidateContext(this);
opengl_context_->ResizeContext(pp::Size(0, 0));
int current = opengl_context_->MakeContextCurrent(this);
printf("current is %i\n", current);
os_nacl = new OSNacl;
pkg_url = "";
for (uint32_t i=0; i<argc; i++) {
if (strcmp(argn[i], "package") == 0) {
pkg_url = argv[i];
};
};
sd = memnew(StateData);
if (pkg_url == "") {
nacl_main(argc, argn, argv);
} else {
printf("starting package %ls\n", pkg_url.c_str());
init_argc = MIN(argc, MAX_ARGS-1);
for (uint32_t i=0; i<argc; i++) {
int nlen = strlen(argn[i]);
init_argn[i] = (char*)memalloc(nlen+1);
strcpy(init_argn[i], argn[i]);
init_argn[i+1] = NULL;
int len = strlen(argv[i]);
init_argv[i] = (char*)memalloc(len+1);
strcpy(init_argv[i], argv[i]);
init_argv[i+1] = NULL;
};
package_pending = memnew(GetURLHandler(this, pkg_url));
package_pending->Start();
};
return true;
};
// Called whenever the in-browser window changes size.
virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
if (position.size().width() == width &&
position.size().height() == height)
return; // Size didn't change, no need to update anything.
if (opengl_context_ == NULL) {
opengl_context_.reset(new OpenGLContext(this));
};
opengl_context_->InvalidateContext(this);
opengl_context_->ResizeContext(position.size());
if (!opengl_context_->MakeContextCurrent(this))
return;
width = position.size().width();
height = position.size().height();
// init gl here?
OS::VideoMode vm;
vm.width = width;
vm.height = height;
vm.resizable = false;
vm.fullscreen = true;
OS::get_singleton()->set_video_mode(vm, 0);
DrawSelf();
};
// Called to draw the contents of the module's browser area.
void DrawSelf() {
if (opengl_context_ == NULL)
return;
opengl_context_->FlushContext();
};
};
static Variant to_variant(const pp::Var& p_var) {
if (p_var.is_undefined() || p_var.is_null())
return Variant();
if (p_var.is_bool())
return Variant(p_var.AsBool());
if (p_var.is_double())
return Variant(p_var.AsDouble());
if (p_var.is_int())
return Variant((int64_t)p_var.AsInt());
if (p_var.is_string())
return Variant(String::utf8(p_var.AsString().c_str()));
return Variant();
};
void GodotInstance::HandleMessage(const pp::Var& var_message) {
switch (state) {
case STATE_METHOD: {
ERR_FAIL_COND(!var_message.is_string());
sd->method = var_message.AsString().c_str();
state = STATE_PARAM_COUNT;
} break;
case STATE_PARAM_COUNT: {
ERR_FAIL_COND(!var_message.is_number());
sd->arg_count = var_message.AsInt();
state = sd->arg_count>0?STATE_PARAMS:STATE_CALL;
} break;
case STATE_PARAMS: {
Variant p = to_variant(var_message);
sd->args.push_back(p);
if (sd->args.size() >= sd->arg_count)
state = STATE_CALL;
} break;
default:
break;
};
if (state == STATE_CALL) {
// call
state = STATE_METHOD;
if (sd->method == "package_finished") {
GetURLHandler::Status status = package_pending->get_status();
printf("status is %i, %i, %i\n", status, GetURLHandler::STATUS_ERROR, GetURLHandler::STATUS_COMPLETED);
if (status == GetURLHandler::STATUS_ERROR) {
printf("Error fetching package!\n");
};
if (status == GetURLHandler::STATUS_COMPLETED) {
OSNacl* os = (OSNacl*)OS::get_singleton();
os->add_package(pkg_url, package_pending->get_data());
};
memdelete(package_pending);
package_pending = NULL;
package_loaded = true;
opengl_context_->MakeContextCurrent(this);
nacl_main(init_argc, (const char**)init_argn, (const char**)init_argv);
for (uint32_t i=0; i<init_argc; i++) {
memfree(init_argn[i]);
memfree(init_argv[i]);
};
};
if (sd->method == "get_package_status") {
if (package_loaded) {
// post "loaded"
PostMessage("loaded");
} else if (package_pending == NULL) {
// post "none"
PostMessage("none");
} else {
// post package_pending->get_bytes_read();
PostMessage(package_pending->get_bytes_read());
};
};
};
}
bool GodotInstance::HandleInputEvent(const pp::InputEvent& event) {
OSNacl* os = (OSNacl*)OS::get_singleton();
os->handle_event(event);
return true;
};
class GodotModule : public pp::Module {
public:
GodotModule() : pp::Module() {}
virtual ~GodotModule() {
glTerminatePPAPI();
}
/// Create and return a GodotInstance object.
/// @param[in] instance a handle to a plug-in instance.
/// @return a newly created GodotInstance.
/// @note The browser is responsible for calling @a delete when done.
virtual pp::Instance* CreateInstance(PP_Instance instance) {
printf("CreateInstance! %x\n", instance);
return new GodotInstance(instance);
}
/// Called by the browser when the module is first loaded and ready to run.
/// This is called once per module, not once per instance of the module on
/// the page.
virtual bool Init() {
printf("GodotModule::init!\n");
return glInitializePPAPI(get_browser_interface());
}
};
namespace pp {
/// Factory function called by the browser when the module is first loaded.
/// The browser keeps a singleton of this module. It calls the
/// CreateInstance() method on the object you return to make instances. There
/// is one instance per <embed> tag on the page. This is the main binding
/// point for your NaCl module with the browser.
/// @return new GodotModule.
/// @note The browser is responsible for deleting returned @a Module.
Module* CreateModule() {
printf("CreateModule!\n");
return new GodotModule();
}
} // namespace pp