static int js_module_read_file(duk_context* ctx) { JSVM* vm = JSVM::GetJSVM(ctx); ResourceCache* cache = vm->GetContext()->GetSubsystem<ResourceCache>(); String path = duk_to_string(ctx, 0); SharedPtr<File> file = cache->GetFile(path); if (!file->IsOpen()) { duk_push_string(ctx, "Unable to open module file"); duk_throw(ctx); return 0; } unsigned size = file->GetSize(); SharedArrayPtr<char> data; data = new char[size + 1]; data[size] = '\0'; file->Read(data, size); duk_push_string(ctx, data); return 1; }
void ClockworkPlayerApp::Start() { Application::Start(); // Instantiate and register NETCore subsystem context_->RegisterSubsystem(new NETCore(context_)); // Instantiate and register the Javascript subsystem Javascript* javascript = new Javascript(context_); context_->RegisterSubsystem(javascript); vm_ = javascript->InstantiateVM("MainVM"); vm_->InitJSContext(); UI* ui = GetSubsystem<UI>(); ui->Initialize("DefaultUI/language/lng_en.tb.txt"); ui->LoadDefaultPlayerSkin(); vm_->SetModuleSearchPaths("Modules"); // Instantiate and register the Player subsystem context_->RegisterSubsystem(new ClockworkPlayer::Player(context_)); ClockworkPlayer::jsapi_init_clockworkplayer(vm_); JSVM* vm = JSVM::GetJSVM(0); if (!vm->ExecuteMain()) { SendEvent(E_EXITREQUESTED); } return; }
static int UIButton_Popup(duk_context* ctx) { if (!duk_is_object(ctx, 0)) { duk_push_string(ctx, "UIButton.popup first argument must be an object"); duk_throw(ctx); } if (!duk_is_callable(ctx, 1)) { duk_push_string(ctx, "UIButton.popup second argument must be callable"); duk_throw(ctx); } JSVM* vm = JSVM::GetJSVM(ctx); duk_enum(ctx, 0, DUK_ENUM_OWN_PROPERTIES_ONLY); UISelectItemSource* source = new UISelectItemSource(vm->GetContext()); while (duk_next(ctx, -1, 0)) { String key = duk_get_string(ctx, -1); duk_get_prop(ctx, 0); if (duk_is_array(ctx, -1)) { // need to support this, for skin image, etc assert(0); } else if (duk_is_string(ctx, -1)) { // id String id = duk_get_string(ctx, -1); source->AddItem(new UISelectItem(vm->GetContext(), key, id)); } else { duk_push_string(ctx, "UIButton.popup data object key is not an array or string"); duk_throw(ctx); } duk_pop(ctx); // pop key value } duk_pop(ctx); // pop enum object duk_push_this(ctx); duk_dup(ctx, 1); duk_put_prop_string(ctx, -2, "__popup_menu_callback"); UIButton* button = js_to_class_instance<UIButton>(ctx, -1, 0); UIMenuWindow* menuWindow = new UIMenuWindow(vm->GetContext(), button, "__popup-menu"); menuWindow->Show(source); duk_pop(ctx); return 0; }
void JSEventDispatcher::EndSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData) { if (!jsEvents_.Contains(eventType)) return; JSVM* vm = JSVM::GetJSVM(NULL); if (!vm) return; duk_context* ctx = vm->GetJSContext(); duk_push_global_stash(ctx); duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_VARIANTMAP_CACHE); duk_push_pointer(ctx, (void*) &eventData); duk_get_prop(ctx, -2); // If this issue is addressed, revisit this to simply remove // the variantmap object from the cache, if the user explicitly // keeps the event object alive in a local closure, that is allowed // (though, will keep object properties from being GC'd) // https://github.com/svaarala/duktape/issues/229 // Ok, this is unfortunate, in an event callback it is possible // to capture the Proxy object which represents the event VariantMap // in a function() {} closure, which will keep the event data alive // until the function happens to be gc'd (it is a member of the eventhandler) // which will leave things like scenes up if there was a P_SCENE event data // member, etc // So, we need to check if we have an object in the variant map cache // and thense call it's delete property method on the Proxy, which will clear // all the data (the proxy can still be alive as a captured local, though // the members won't be held // This all makes it that much more important that the pointer to eventData // is consistent across entire event, otherwise references may be held // see note above about: https://github.com/svaarala/duktape/issues/229 if (duk_is_object(ctx, -1)) { // deletes all properties, thus freeing references, even if // the variant map object is held onto by script (it will be invalid, post // event send) // see JSAPI.cpp variantmap_property_deleteproperty duk_del_prop_index(ctx, -1, 0); duk_push_pointer(ctx, (void*) &eventData); duk_push_undefined(ctx); // clear the variant map object from the cache duk_put_prop(ctx, -4); } duk_pop_3(ctx); }
static int js_print(duk_context* ctx) { duk_concat(ctx, duk_get_top(ctx)); VariantMap eventData; using namespace JSPrint; eventData[P_TEXT] = duk_to_string(ctx, -1); JSVM* vm = JSVM::GetJSVM(ctx); vm->SendEvent(E_JSPRINT, eventData); LOGINFOF("%s", duk_to_string(ctx, -1)); return 0; }
void AEPlayerApplication::Start() { AEEditorCommon::Start(); UI* ui = GetSubsystem<UI>(); ui->Initialize("DefaultUI/language/lng_en.tb.txt"); ui->LoadDefaultPlayerSkin(); context_->RegisterSubsystem(new PlayerMode(context_)); PlayerMode* playerMode = GetSubsystem<PlayerMode>(); playerMode->ProcessArguments(); SubscribeToEvent(E_JSERROR, HANDLER(AEPlayerApplication, HandleJSError)); #ifdef ATOMIC_DOTNET if (debugPlayer_) { GetSubsystem<NETCore>()->WaitForDebuggerConnect(); } #endif vm_->SetModuleSearchPaths("Modules"); // Instantiate and register the Player subsystem context_->RegisterSubsystem(new AtomicPlayer::Player(context_)); AtomicPlayer::jsapi_init_atomicplayer(vm_); #ifdef ATOMIC_DOTNET // Initialize Scripting Subsystem NETScript* netScript = new NETScript(context_); context_->RegisterSubsystem(netScript); netScript->Initialize(); netScript->ExecMainAssembly(); #endif if (!playerMode->launchedByEditor()) { JSVM* vm = JSVM::GetJSVM(0); if (!vm->ExecuteMain()) { SendEvent(E_EXITREQUESTED); } } SubscribeToEvent(E_PLAYERQUIT, HANDLER(AEPlayerApplication, HandleQuit)); return; }
void IPCPlayerApp::Start() { if (subprocess_) { // do not execute main in the player app executeJSMain_ = false; } PlayerApp::Start(); int id = -1; if (IPC::ProcessArguments(arguments_, id, fd_[0], fd_[1])) { SubscribeToEvent(E_IPCINITIALIZE, ATOMIC_HANDLER(IPCPlayerApp, HandleIPCInitialize)); SubscribeToEvent(E_LOGMESSAGE, ATOMIC_HANDLER(IPCPlayerApp, HandleLogMessage)); SubscribeToEvent(E_JSERROR, ATOMIC_HANDLER(IPCPlayerApp, HandleJSError)); SubscribeToEvent(E_EXITREQUESTED, ATOMIC_HANDLER(IPCPlayerApp, HandleExitRequest)); SubscribeToEvent(E_SCREENMODE, ATOMIC_HANDLER(IPCPlayerApp, HandlePlayerWindowChanged)); SubscribeToEvent(E_WINDOWPOS, ATOMIC_HANDLER(IPCPlayerApp, HandlePlayerWindowChanged)); SubscribeToEvent(E_UPDATESPAUSEDRESUMED, ATOMIC_HANDLER(IPCPlayerApp, HandleUpdatesPausedResumed)); if (ipc_->InitWorker((unsigned)id, fd_[0], fd_[1])) { brokerActive_ = true; } else if (subprocess_) { ATOMIC_LOGERROR("IPCPlayerApp::Start() - Unable to initialize IPC Worker"); } } if (subprocess_) { JSVM* vm = JSVM::GetJSVM(0); if (!vm->ExecuteMain()) { SendEvent(E_EXITREQUESTED); } SubscribeToEvent(E_PLAYERQUIT, ATOMIC_HANDLER(IPCPlayerApp, HandleQuit)); } GetSubsystem<Graphics>()->RaiseWindow(); }
static int js_atomic_script(duk_context* ctx) { JSVM* vm = JSVM::GetJSVM(ctx); if (duk_is_string(ctx, 0)) { if ( vm->ExecuteScript(duk_to_string(ctx, 0))) duk_push_boolean(ctx, 1); else duk_push_boolean(ctx, 0); } else duk_push_boolean(ctx, 0); return 1; }
void PlayerMode::HandleIPCInitialize(StringHash eventType, VariantMap& eventData) { brokerActive_ = true; JSVM* vm = JSVM::GetJSVM(0); if (!vm->ExecuteMain()) { SendEvent(E_EXITREQUESTED); } // BEGIN LICENSE MANAGEMENT licenseModule3D_ = eventData["license3D"].GetBool(); // END LICENSE MANAGEMENT SystemUI::DebugHud* debugHud = GetSubsystem<SystemUI::DebugHud>(); if (debugHud) debugHud->SetMode(eventData["debugHudMode"].GetUInt()); }
// see http://duktape.org/guide.html#modules static int js_module_search(duk_context* ctx) { JSVM* vm = JSVM::GetJSVM(ctx); FileSystem* fs = vm->GetSubsystem<FileSystem>(); ResourceCache* cache = vm->GetSubsystem<ResourceCache>(); int top = duk_get_top(ctx); assert(top == 4); String moduleID = duk_to_string(ctx, 0); if (top > 1) { // require function assert(duk_is_function(ctx, 1)); } if (top > 2) { // exports assert(duk_is_object(ctx, 2)); } if (top > 3) { // module (module.id == a resolved absolute identifier for the module being loaded) assert(duk_is_object(ctx, 3)); } String pathName, fileName, extension; SplitPath(moduleID, pathName, fileName, extension); String path = moduleID; // Do we really want this? It is nice to not have to specify the Atomic path if (fileName.StartsWith("Atomic")) { path = "AtomicModules/" + path + ".js"; } else { path += ".js"; if (!cache->Exists(path)) { const Vector<String>& searchPaths = vm->GetModuleSearchPaths(); for (unsigned i = 0; i < searchPaths.Size(); i++) { String search = searchPaths[i] + path; if (cache->Exists(search)) { path = search; break; } } } } if (cache->Exists(path)) { SharedPtr<File> jsfile(cache->GetFile(path, false)); vm->SetLastModuleSearchFile(jsfile->GetFullPath()); String source; jsfile->ReadText(source); source.Append('\n'); duk_push_string(ctx, source.CString()); return 1; } else { // we're not a JS file, so check if we're a native module const Vector<String>& resourceDirs = cache->GetResourceDirs(); for (unsigned i = 0; i < resourceDirs.Size(); i++) { String pluginLibrary; // TODO: proper platform folder detection #ifdef ATOMIC_PLATFORM_WINDOWS pluginLibrary = resourceDirs.At(i) + "Plugins/Windows/x64/" + moduleID + ".dll"; #elif ATOMIC_PLATFORM_OSX pluginLibrary = resourceDirs.At(i) + "Plugins/Mac/x64/lib" + moduleID + ".dylib"; #endif if (pluginLibrary.Length() && fs->FileExists(pluginLibrary)) { // let duktape know we loaded a native module if (jsplugin_load(vm, pluginLibrary)) { duk_push_undefined(ctx); return 1; } else { duk_push_sprintf(ctx, "Failed loading native plugins: %s", pluginLibrary.CString()); duk_throw(ctx); } } } } duk_push_sprintf(ctx, "Failed loading module: %s", path.CString()); duk_throw(ctx); }
void JSEventHelper::HandleEvent(StringHash eventType, VariantMap& eventData) { if (object_.Null()) return; JSVM* vm = JSVM::GetJSVM(0); duk_context* ctx = vm->GetJSContext(); duk_idx_t top = duk_get_top(ctx); js_push_class_object_instance(ctx, this); duk_get_prop_string(ctx, -1, "__eventHelperFunctions"); assert(duk_is_object(ctx, -1)); duk_get_prop_string(ctx, -1, eventType.ToString().CString()); if (duk_is_function(ctx, -1)) { // look in the variant map cache duk_push_global_stash(ctx); duk_get_prop_index(ctx, -1, JS_GLOBALSTASH_VARIANTMAP_CACHE); duk_push_pointer(ctx, (void*) &eventData); duk_get_prop(ctx, -2); if (!duk_is_object(ctx, -1)) { // pop result duk_pop(ctx); // we need to push a new variant map and store to cache // the cache object will be cleared at the send end in the // global listener above js_push_variantmap(ctx, eventData); duk_push_pointer(ctx, (void*) &eventData); duk_dup(ctx, -2); duk_put_prop(ctx, -4); } duk_remove(ctx, -2); // vmap cache duk_remove(ctx, -2); // global stash if (duk_pcall(ctx, 1) != 0) { vm->SendJSErrorEvent(); } else { // For widget events, need to check return value // and set whether handled if (eventType == E_WIDGETEVENT) { if (duk_is_boolean(ctx, -1)) { if (duk_to_boolean(ctx, -1)) { eventData[WidgetEvent::P_HANDLED] = true; } } } } } duk_set_top(ctx, top); }