diff --git a/data/gui.xml b/data/gui.xml index 953aa9efd..167a20a02 100644 --- a/data/gui.xml +++ b/data/gui.xml @@ -535,6 +535,11 @@ + + + + + diff --git a/data/scripts/white_to_alpha.js b/data/scripts/white_to_alpha.js new file mode 100644 index 000000000..a469391f7 --- /dev/null +++ b/data/scripts/white_to_alpha.js @@ -0,0 +1,15 @@ +// Aseprite +// Copyright (C) 2015 by David Capello + +var spr = activeSprite + +for (y=0; yexecuteCommand(command); } } + // --script + else if (opt == &options.script()) { + std::string script = value.value(); + + StdoutEngineDelegate delegate; + AppScripting engine(&delegate); + engine.evalFile(script); + } } // File names aren't associated to any option else { @@ -540,7 +548,7 @@ void App::run() // Start shell to execute scripts. if (m_isShell) { StdoutEngineDelegate delegate; - scripting::Engine engine(&delegate); + AppScripting engine(&delegate); Shell shell; shell.run(engine); } diff --git a/src/app/app_options.cpp b/src/app/app_options.cpp index ad99b91b3..c5f32618f 100644 --- a/src/app/app_options.cpp +++ b/src/app/app_options.cpp @@ -46,6 +46,7 @@ AppOptions::AppOptions(int argc, const char* argv[]) , m_trim(m_po.add("trim").description("Trim all images before exporting")) , m_crop(m_po.add("crop").requiresValue("x,y,width,height").description("Crop all the images to the given rectangle")) , m_filenameFormat(m_po.add("filename-format").requiresValue("").description("Special format to generate filenames")) + , m_script(m_po.add("script").requiresValue("").description("Execute a specific script")) , m_verbose(m_po.add("verbose").mnemonic('v').description("Explain what is being done")) , m_help(m_po.add("help").mnemonic('?').description("Display this help and exits")) , m_version(m_po.add("version").description("Output version information and exit")) diff --git a/src/app/app_options.h b/src/app/app_options.h index 9da1968f7..0d672948f 100644 --- a/src/app/app_options.h +++ b/src/app/app_options.h @@ -54,6 +54,7 @@ public: const Option& trim() const { return m_trim; } const Option& crop() const { return m_crop; } const Option& filenameFormat() const { return m_filenameFormat; } + const Option& script() const { return m_script; } bool hasExporterParams() const; @@ -89,6 +90,7 @@ private: Option& m_trim; Option& m_crop; Option& m_filenameFormat; + Option& m_script; Option& m_verbose; Option& m_help; diff --git a/src/app/commands/cmd_run_script.cpp b/src/app/commands/cmd_run_script.cpp new file mode 100644 index 000000000..2e1b68225 --- /dev/null +++ b/src/app/commands/cmd_run_script.cpp @@ -0,0 +1,80 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/commands/command.h" +#include "app/commands/params.h" +#include "app/console.h" +#include "app/resource_finder.h" +#include "app/scripting/app_scripting.h" +#include "base/path.h" +#include "scripting/engine_delegate.h" +#include "ui/manager.h" + +#include + +namespace app { + +class ConsoleEngineDelegate : public scripting::EngineDelegate { +public: + void onConsolePrint(const char* text) override { + m_console.printf("%s\n", text); + } + +private: + Console m_console; +}; + +class RunScriptCommand : public Command { +public: + RunScriptCommand(); + Command* clone() const override { return new RunScriptCommand(*this); } + +protected: + void onLoadParams(const Params& params) override; + void onExecute(Context* context) override; + +private: + std::string m_filename; +}; + +RunScriptCommand::RunScriptCommand() + : Command("RunScript", + "Run Script", + CmdRecordableFlag) +{ +} + +void RunScriptCommand::onLoadParams(const Params& params) +{ + m_filename = params.get("filename"); + if (base::get_file_path(m_filename).empty()) { + ResourceFinder rf; + rf.includeDataDir(base::join_path("scripts", m_filename).c_str()); + if (rf.findFirst()) + m_filename = rf.filename(); + } +} + +void RunScriptCommand::onExecute(Context* context) +{ + ConsoleEngineDelegate delegate; + AppScripting engine(&delegate); + engine.evalFile(m_filename); + + ui::Manager::getDefault()->invalidate(); +} + +Command* CommandFactory::createRunScriptCommand() +{ + return new RunScriptCommand; +} + +} // namespace app diff --git a/src/app/commands/commands_list.h b/src/app/commands/commands_list.h index a7c88cfbb..fc56df7f9 100644 --- a/src/app/commands/commands_list.h +++ b/src/app/commands/commands_list.h @@ -94,6 +94,7 @@ FOR_EACH_COMMAND(ReplaceColor) FOR_EACH_COMMAND(ReselectMask) FOR_EACH_COMMAND(ReverseFrames) FOR_EACH_COMMAND(Rotate) +FOR_EACH_COMMAND(RunScript) FOR_EACH_COMMAND(SaveFile) FOR_EACH_COMMAND(SaveFileAs) FOR_EACH_COMMAND(SaveFileCopyAs) diff --git a/src/app/scripting/app_scripting.cpp b/src/app/scripting/app_scripting.cpp new file mode 100644 index 000000000..386ab50cc --- /dev/null +++ b/src/app/scripting/app_scripting.cpp @@ -0,0 +1,40 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/scripting/app_scripting.h" + +#include "app/document.h" +#include "app/document_api.h" +#include "app/transaction.h" +#include "app/ui/document_view.h" +#include "app/ui_context.h" +#include "doc/color.h" +#include "doc/image.h" +#include "doc/site.h" + +namespace app { + +#include "app/scripting/raw_color.h" +#include "app/scripting/sprite.h" + +AppScripting::AppScripting(scripting::EngineDelegate* delegate) + : scripting::Engine(delegate) +{ + registerFunction("rgba", rgba, 4); + registerFunction("rgbaR", rgbaR, 1); + registerFunction("rgbaG", rgbaG, 1); + registerFunction("rgbaB", rgbaB, 1); + registerFunction("rgbaA", rgbaA, 1); + registerClass("Sprite", Sprite_ctor, 3, Sprite_methods, Sprite_props); + registerGlobal("activeSprite", activeSprite_getter, activeSprite_setter); +} + +} diff --git a/src/app/scripting/app_scripting.h b/src/app/scripting/app_scripting.h new file mode 100644 index 000000000..37abf827c --- /dev/null +++ b/src/app/scripting/app_scripting.h @@ -0,0 +1,24 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifndef APP_SCRIPTING_H_INCLUDED +#define APP_SCRIPTING_H_INCLUDED +#pragma once + +#include "scripting/engine.h" + +namespace app { + class Document; + + class AppScripting : public scripting::Engine { + public: + AppScripting(scripting::EngineDelegate* delegate); + }; + +} // namespace app + +#endif diff --git a/src/app/scripting/raw_color.h b/src/app/scripting/raw_color.h new file mode 100644 index 000000000..1bfbb1c51 --- /dev/null +++ b/src/app/scripting/raw_color.h @@ -0,0 +1,49 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +namespace { + +scripting::result_t rgba(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + int r = ctx.requireInt(0); + int g = ctx.requireInt(1); + int b = ctx.requireInt(2); + int a = (ctx.isUndefined(3) ? 255: ctx.requireInt(3)); + ctx.pushUInt(doc::rgba(r, g, b, a)); + return 1; +} + +scripting::result_t rgbaR(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + ctx.pushUInt(doc::rgba_getr(ctx.requireUInt(0))); + return 1; +} + +scripting::result_t rgbaG(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + ctx.pushUInt(doc::rgba_getg(ctx.requireUInt(0))); + return 1; +} + +scripting::result_t rgbaB(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + ctx.pushUInt(doc::rgba_getb(ctx.requireUInt(0))); + return 1; +} + +scripting::result_t rgbaA(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + ctx.pushUInt(doc::rgba_geta(ctx.requireUInt(0))); + return 1; +} + +} // anonymous namespace diff --git a/src/app/scripting/sprite.h b/src/app/scripting/sprite.h new file mode 100644 index 000000000..dceafa05e --- /dev/null +++ b/src/app/scripting/sprite.h @@ -0,0 +1,143 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +namespace { + +scripting::result_t Sprite_ctor(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + if (ctx.isConstructorCall()) { + int w = ctx.requireInt(0); + int h = ctx.requireInt(1); + int colorMode = (ctx.isUndefined(2) ? IMAGE_RGB: ctx.requireInt(2)); + + base::UniquePtr sprite( + Sprite::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256)); + base::UniquePtr doc(new Document(sprite)); + sprite.release(); + + doc->setContext(UIContext::instance()); + ctx.pushThis(doc.release()); + } + return 0; +} + +scripting::result_t Sprite_putPixel(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + int x = ctx.requireInt(0); + int y = ctx.requireInt(1); + doc::color_t color = ctx.requireUInt(2); + + Document* doc = (Document*)ctx.getThis(); + DocumentView* docView = UIContext::instance()->getFirstDocumentView(doc); + if (!docView) + return 0; + + doc::Site site; + docView->getSite(&site); + + int celX, celY; + doc::Image* image = site.image(&celX, &celY, nullptr); + if (image) + image->putPixel(x-celX, y-celY, color); + + return 0; +} + +scripting::result_t Sprite_getPixel(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + int x = ctx.requireInt(0); + int y = ctx.requireInt(1); + + Document* doc = (Document*)ctx.getThis(); + DocumentView* docView = UIContext::instance()->getFirstDocumentView(doc); + if (!docView) + return 0; + + doc::Site site; + docView->getSite(&site); + + int celX, celY; + doc::Image* image = site.image(&celX, &celY, nullptr); + if (image) { + doc::color_t color = image->getPixel(x-celX, y-celY); + ctx.pushUInt(color); + return 1; + } + else + return 0; +} + +scripting::result_t Sprite_resize(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + int w = ctx.requireInt(0); + int h = ctx.requireInt(1); + + Document* doc = (Document*)ctx.getThis(); + { + Transaction transaction(UIContext::instance(), "Script Execution", ModifyDocument); + DocumentApi api(doc, transaction); + api.setSpriteSize(doc->sprite(), w, h); + transaction.commit(); + } + + return 0; +} + +scripting::result_t Sprite_get_width(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + Document* doc = (Document*)ctx.getThis(); + ctx.pushInt(doc->sprite()->width()); + return 1; +} + +scripting::result_t Sprite_get_height(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + Document* doc = (Document*)ctx.getThis(); + ctx.pushInt(doc->sprite()->height()); + return 1; +} + +scripting::result_t activeSprite_getter(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + app::Document* doc = UIContext::instance()->activeDocument(); + if (doc) + ctx.pushObject(doc, "Sprite"); + else + ctx.pushNull(); + return 1; +} + +scripting::result_t activeSprite_setter(scripting::ContextHandle handle) +{ + scripting::Context ctx(handle); + Document* doc = (Document*)ctx.requireObject(0, "Sprite"); + if (doc) + UIContext::instance()->setActiveDocument(doc); + return 0; +} + +const scripting::FunctionEntry Sprite_methods[] = { + { "putPixel", Sprite_putPixel, 3 }, + { "getPixel", Sprite_getPixel, 2 }, + { "resize", Sprite_resize, 2 }, + { nullptr, nullptr, 0 } +}; + +const scripting::PropertyEntry Sprite_props[] = { + { "width", Sprite_get_width, nullptr }, + { "height", Sprite_get_height, nullptr }, + { nullptr, nullptr, 0 } +}; + +} // anonymous namespace diff --git a/src/app/ui/devconsole_view.h b/src/app/ui/devconsole_view.h index 06f4c0c6f..9bcd0b02f 100644 --- a/src/app/ui/devconsole_view.h +++ b/src/app/ui/devconsole_view.h @@ -9,9 +9,9 @@ #define APP_UI_DEVCONSOLE_VIEW_H_INCLUDED #pragma once +#include "app/scripting/app_scripting.h" #include "app/ui/tabs.h" #include "app/ui/workspace_view.h" -#include "scripting/engine.h" #include "scripting/engine_delegate.h" #include "ui/box.h" #include "ui/label.h" @@ -54,7 +54,7 @@ namespace app { ui::HBox m_bottomBox; ui::Label m_label; CommmandEntry* m_entry; - scripting::Engine m_engine; + AppScripting m_engine; }; } // namespace app diff --git a/src/scripting/engine.cpp b/src/scripting/engine.cpp index 7fc100342..c5698ac61 100644 --- a/src/scripting/engine.cpp +++ b/src/scripting/engine.cpp @@ -4,22 +4,354 @@ // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "scripting/engine.h" -#include "scripting/engine_duktape.h" +#include "base/convert_to.h" +#include "base/exception.h" +#include "base/file_handle.h" +#include "base/memory.h" +#include "scripting/engine_delegate.h" + +#define DUK_OPT_PANIC_HANDLER scripting_engine_panic_handler +#include + +class ScriptingEngineException : public base::Exception { +public: + ScriptingEngineException(duk_errcode_t code, const char* msg) + : Exception(std::string(msg) + " (code " + base::convert_to(code) + ")") { + } +}; + +static void scripting_engine_panic_handler(duk_errcode_t code, const char* msg) +{ + throw ScriptingEngineException(code, msg); +} + +extern "C" { + #include +} namespace scripting { - Engine::Engine(EngineDelegate* delegate) - : m_impl(new EngineImpl(delegate)) { +bool Context::isConstructorCall() +{ + return (duk_is_constructor_call(m_handle) ? true: false); +} + +bool Context::isUndefined(index_t i) +{ + return (duk_is_undefined(m_handle, i) ? true: false); +} + +bool Context::isNull(index_t i) +{ + return (duk_is_null(m_handle, i) ? true: false); +} + +bool Context::isNullOrUndefined(index_t i) +{ + return (duk_is_null_or_undefined(m_handle, i) ? true: false); +} + +bool Context::isBool(index_t i) +{ + return (duk_is_boolean(m_handle, i) ? true: false); +} + +bool Context::isNumber(index_t i) +{ + return (duk_is_number(m_handle, i) ? true: false); +} + +bool Context::isNaN(index_t i) +{ + return (duk_is_nan(m_handle, i) ? true: false); +} + +bool Context::isString(index_t i) +{ + return (duk_is_string(m_handle, i) ? true: false); +} + +bool Context::getBool(index_t i) +{ + return (duk_get_boolean(m_handle, i) ? true: false); +} + +double Context::getNumber(index_t i) +{ + return duk_get_number(m_handle, i); +} + +int Context::getInt(index_t i) +{ + return duk_get_int(m_handle, i); +} + +unsigned int Context::getUInt(index_t i) +{ + return duk_get_uint(m_handle, i); +} + +const char* Context::getString(index_t i) +{ + return duk_get_string(m_handle, i); +} + +bool Context::requireBool(index_t i) +{ + return (duk_require_boolean(m_handle, i) ? true: false); +} + +double Context::requireNumber(index_t i) +{ + return duk_require_number(m_handle, i); +} + +int Context::requireInt(index_t i) +{ + return duk_require_int(m_handle, i); +} + +unsigned int Context::requireUInt(index_t i) +{ + return duk_require_uint(m_handle, i); +} + +const char* Context::requireString(index_t i) +{ + return duk_require_string(m_handle, i); +} + +void* Context::requireObject(index_t i, const char* className) +{ + duk_get_prop_string(m_handle, i, "\xFF" "\xFF" "ptr"); + void* result = (void*)duk_to_pointer(m_handle, -1); + // TODO check pointer type + duk_pop(m_handle); + return result; +} + +void Context::pushUndefined() +{ + duk_push_undefined(m_handle); +} + +void Context::pushNull() +{ + duk_push_null(m_handle); +} + +void Context::pushBool(bool val) +{ + duk_push_boolean(m_handle, val); +} + +void Context::pushNumber(double val) +{ + duk_push_number(m_handle, val); +} + +void Context::pushNaN() +{ + duk_push_nan(m_handle); +} + +void Context::pushInt(int val) +{ + duk_push_int(m_handle, val); +} + +void Context::pushUInt(unsigned int val) +{ + duk_push_uint(m_handle, val); +} + +void Context::pushString(const char* str) +{ + duk_push_string(m_handle, str); +} + +void Context::pushThis(void* ptr) +{ + duk_push_this(m_handle); + duk_push_pointer(m_handle, ptr); + duk_put_prop_string(m_handle, -2, "\xFF" "\xFF" "ptr"); +} + +void Context::pushObject(void* ptr, const char* className) +{ + duk_push_object(m_handle); + duk_push_pointer(m_handle, ptr); + duk_put_prop_string(m_handle, -2, "\xFF" "\xFF" "ptr"); + + duk_get_global_string(m_handle, className); + duk_get_prop_string(m_handle, -1, "prototype"); + duk_set_prototype(m_handle, -3); + duk_pop(m_handle); +} + +void* Context::getThis() +{ + duk_push_this(m_handle); + duk_get_prop_string(m_handle, -1, "\xFF" "\xFF" "ptr"); + void* result = (void*)duk_to_pointer(m_handle, -1); + duk_pop(m_handle); + return result; +} + +Engine::Engine(EngineDelegate* delegate) + : m_delegate(delegate) + , m_ctx(duk_create_heap_default()) +{ +} + +Engine::~Engine() +{ + duk_destroy_heap(m_ctx.handle()); +} + +void Engine::eval(const std::string& jsCode) +{ + try { + ContextHandle handle = m_ctx.handle(); + + duk_eval_string(handle, jsCode.c_str()); + + if (!duk_is_null_or_undefined(handle, -1)) + m_delegate->onConsolePrint(duk_safe_to_string(handle, -1)); + + duk_pop(handle); + } + catch (const std::exception& ex) { + std::string err = "Error: "; + err += ex.what(); + m_delegate->onConsolePrint(err.c_str()); + } +} + +void Engine::evalFile(const std::string& file) +{ + try { + ContextHandle handle = m_ctx.handle(); + + FILE* f = base::open_file_raw(file, "rb"); + if (!f) + goto fail; + + if (fseek(f, 0, SEEK_END) < 0) + goto fail; + std::size_t sz = ftell(f); + if (sz < 0) + goto fail; + if (fseek(f, 0, SEEK_SET) < 0) + goto fail; + + char* buf = (char*)duk_push_fixed_buffer(handle, sz); + ASSERT(buf != nullptr); + if (fread(buf, 1, sz, f) != sz) + goto fail; + + fclose(f); + f = nullptr; + + duk_push_string(handle, duk_to_string(handle, -1)); + duk_eval_raw(handle, nullptr, 0, DUK_COMPILE_EVAL); + + if (!duk_is_null_or_undefined(handle, -1)) + m_delegate->onConsolePrint(duk_safe_to_string(handle, -1)); + + duk_pop(handle); + + fail: + if (f) + fclose(f); + } + catch (const std::exception& ex) { + std::string err = "Error: "; + err += ex.what(); + m_delegate->onConsolePrint(err.c_str()); + } +} + +void Engine::registerFunction(const char* id, Function function, int nargs) +{ + ContextHandle handle = m_ctx.handle(); + + duk_push_global_object(handle); + duk_push_c_function(handle, function, nargs); + duk_put_prop_string(handle, -2, id); +} + +void Engine::registerClass(const char* id, + Function ctorFunc, int ctorNargs, + const FunctionEntry* methods, + const PropertyEntry* props) +{ + ContextHandle handle = m_ctx.handle(); + duk_push_c_function(handle, ctorFunc, ctorNargs); + + duk_push_object(handle); + + if (methods) + duk_put_function_list(handle, -1, (const duk_function_list_entry*)methods); + + if (props) { + for (int i=0; props[i].key; ++i) { + duk_push_string(handle, props[i].key); + + duk_idx_t objidx = -2; + duk_uint_t flags = 0; + + if (props[i].getter) { + flags |= DUK_DEFPROP_HAVE_GETTER; + duk_push_c_function(handle, props[i].getter, 0); + --objidx; + } + + if (props[i].setter) { + flags |= DUK_DEFPROP_HAVE_SETTER; + duk_push_c_function(handle, props[i].setter, 1); + --objidx; + } + + duk_def_prop(handle, objidx, flags); + } } - Engine::~Engine() { - delete m_impl; + duk_put_prop_string(handle, -2, "prototype"); + + duk_put_global_string(handle, id); +} + +void Engine::registerGlobal(const char* id, + Function getter, Function setter) +{ + ContextHandle handle = m_ctx.handle(); + + duk_push_global_object(handle); + duk_push_string(handle, id); + + duk_idx_t objidx = -2; + duk_uint_t flags = 0; + + if (getter) { + flags |= DUK_DEFPROP_HAVE_GETTER; + duk_push_c_function(handle, getter, 0); + --objidx; } - void Engine::eval(const std::string& script) { - m_impl->eval(script); + if (setter) { + flags |= DUK_DEFPROP_HAVE_SETTER; + duk_push_c_function(handle, setter, 1); + --objidx; } + duk_def_prop(handle, objidx, flags); +} + } // namespace scripting diff --git a/src/scripting/engine.h b/src/scripting/engine.h index 46aeb7e80..992eb7e06 100644 --- a/src/scripting/engine.h +++ b/src/scripting/engine.h @@ -13,16 +13,89 @@ namespace scripting { class EngineDelegate; + typedef int result_t; + typedef int index_t; + + typedef void* ContextHandle; + + class Context { + public: + Context(ContextHandle handle) : m_handle(handle) { } + + ContextHandle handle() { return m_handle; } + + bool isConstructorCall(); + + bool isUndefined(index_t i); + bool isNull(index_t i); + bool isNullOrUndefined(index_t i); + bool isBool(index_t i); + bool isNumber(index_t i); + bool isNaN(index_t i); + bool isString(index_t i); + + bool getBool(index_t i); + double getNumber(index_t i); + int getInt(index_t i); + unsigned int getUInt(index_t i); + const char* getString(index_t i); + void* getThis(); + + bool requireBool(index_t i); + double requireNumber(index_t i); + int requireInt(index_t i); + unsigned int requireUInt(index_t i); + const char* requireString(index_t i); + void* requireObject(index_t i, const char* className); + + void pushUndefined(); + void pushNull(); + void pushBool(bool val); + void pushNumber(double val); + void pushNaN(); + void pushInt(int val); + void pushUInt(unsigned int val); + void pushString(const char* str); + void pushThis(void* ptr); + void pushObject(void* ptr, const char* className); + + private: + ContextHandle m_handle; + }; + + typedef result_t (*Function)(ContextHandle ctx); + + struct FunctionEntry { + const char* key; + Function value; + index_t nargs; + }; + + struct PropertyEntry { + const char* key; + Function getter; + Function setter; + }; + class Engine { public: Engine(EngineDelegate* delegate); ~Engine(); - void eval(const std::string& script); + void eval(const std::string& jsCode); + void evalFile(const std::string& file); + + void registerFunction(const char* id, Function function, int nargs); + void registerClass(const char* id, + Function ctorFunc, int ctorNargs, + const FunctionEntry* methods, + const PropertyEntry* props); + void registerGlobal(const char* id, + Function getter, Function setter); private: - class EngineImpl; - EngineImpl* m_impl; + Context m_ctx; + EngineDelegate* m_delegate; }; } diff --git a/src/scripting/engine_duktape.h b/src/scripting/engine_duktape.h deleted file mode 100644 index 7d8d33459..000000000 --- a/src/scripting/engine_duktape.h +++ /dev/null @@ -1,62 +0,0 @@ -// Aseprite Scripting Library -// Copyright (c) 2015 David Capello -// -// This file is released under the terms of the MIT license. -// Read LICENSE.txt for more information. - -#include "scripting/engine.h" - -#include "base/convert_to.h" -#include "base/exception.h" -#include "base/memory.h" -#include "scripting/engine_delegate.h" - -#define DUK_OPT_PANIC_HANDLER scripting_engine_panic_handler -#include - -class ScriptingEngineException : public base::Exception { -public: - ScriptingEngineException(duk_errcode_t code, const char* msg) - : Exception(std::string(msg) + " (code " + base::convert_to(code) + ")") { - } -}; - -static void scripting_engine_panic_handler(duk_errcode_t code, const char* msg) -{ - throw ScriptingEngineException(code, msg); -} - -extern "C" { - #include -} - -class scripting::Engine::EngineImpl { -private: - -public: - EngineImpl(EngineDelegate* delegate) - : m_delegate(delegate) { - m_ctx = duk_create_heap_default(); - } - - ~EngineImpl() { - duk_destroy_heap(m_ctx); - } - - void eval(const std::string& scriptString) { - try { - duk_eval_string(m_ctx, scriptString.c_str()); - m_delegate->onConsolePrint(duk_safe_to_string(m_ctx, -1)); - duk_pop(m_ctx); - } - catch (const std::exception& ex) { - std::string err = "Error: "; - err += ex.what(); - m_delegate->onConsolePrint(err.c_str()); - } - } - -private: - duk_context* m_ctx; - EngineDelegate* m_delegate; -};