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;
-};