mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-01 10:21:04 +00:00
Merge branch 'scripting'
This commit is contained in:
commit
f8f1aba806
@ -535,6 +535,11 @@
|
||||
<separator />
|
||||
<item command="Despeckle" text="&Despeckle (median filter)" />
|
||||
</menu>
|
||||
<menu text="Scripts">
|
||||
<item command="RunScript" text="Transparency from White Background">
|
||||
<param name="filename" value="white_to_alpha.js" />
|
||||
</item>
|
||||
</menu>
|
||||
<separator />
|
||||
<item command="KeyboardShortcuts" text="&Keyboard Shortcuts..." />
|
||||
<item command="Options" text="Pre&ferences..." />
|
||||
|
15
data/scripts/white_to_alpha.js
Normal file
15
data/scripts/white_to_alpha.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2015 by David Capello
|
||||
|
||||
var spr = activeSprite
|
||||
|
||||
for (y=0; y<spr.height; ++y) {
|
||||
for (x=0; x<spr.width; ++x) {
|
||||
var c = spr.getPixel(x, y)
|
||||
var v = (rgbaR(c)+rgbaG(c)+rgbaB(c))/3
|
||||
spr.putPixel(x, y, rgba(rgbaR(c),
|
||||
rgbaG(c),
|
||||
rgbaB(c),
|
||||
255-v))
|
||||
}
|
||||
}
|
@ -217,6 +217,7 @@ add_library(app-lib
|
||||
commands/cmd_reselect_mask.cpp
|
||||
commands/cmd_reverse_frames.cpp
|
||||
commands/cmd_rotate.cpp
|
||||
commands/cmd_run_script.cpp
|
||||
commands/cmd_save_file.cpp
|
||||
commands/cmd_save_mask.cpp
|
||||
commands/cmd_save_palette.cpp
|
||||
@ -295,6 +296,7 @@ add_library(app-lib
|
||||
res/palettes_loader_delegate.cpp
|
||||
res/resources_loader.cpp
|
||||
resource_finder.cpp
|
||||
scripting/app_scripting.cpp
|
||||
send_crash.cpp
|
||||
shell.cpp
|
||||
snap_to_grid.cpp
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/recent_files.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/scripting/app_scripting.h"
|
||||
#include "app/send_crash.h"
|
||||
#include "app/shell.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
@ -65,7 +66,6 @@
|
||||
#include "doc/site.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/render.h"
|
||||
#include "scripting/engine.h"
|
||||
#include "scripting/engine_delegate.h"
|
||||
#include "she/display.h"
|
||||
#include "she/error.h"
|
||||
@ -436,6 +436,14 @@ void App::initialize(const AppOptions& options)
|
||||
ctx->executeCommand(command);
|
||||
}
|
||||
}
|
||||
// --script <filename>
|
||||
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);
|
||||
}
|
||||
|
@ -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("<fmt>").description("Special format to generate filenames"))
|
||||
, m_script(m_po.add("script").requiresValue("<filename>").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"))
|
||||
|
@ -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;
|
||||
|
80
src/app/commands/cmd_run_script.cpp
Normal file
80
src/app/commands/cmd_run_script.cpp
Normal file
@ -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 <cstdio>
|
||||
|
||||
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
|
@ -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)
|
||||
|
40
src/app/scripting/app_scripting.cpp
Normal file
40
src/app/scripting/app_scripting.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
24
src/app/scripting/app_scripting.h
Normal file
24
src/app/scripting/app_scripting.h
Normal file
@ -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
|
49
src/app/scripting/raw_color.h
Normal file
49
src/app/scripting/raw_color.h
Normal file
@ -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
|
143
src/app/scripting/sprite.h
Normal file
143
src/app/scripting/sprite.h
Normal file
@ -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(
|
||||
Sprite::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256));
|
||||
base::UniquePtr<Document> 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
|
@ -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
|
||||
|
@ -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 <duktape.h>
|
||||
|
||||
class ScriptingEngineException : public base::Exception {
|
||||
public:
|
||||
ScriptingEngineException(duk_errcode_t code, const char* msg)
|
||||
: Exception(std::string(msg) + " (code " + base::convert_to<std::string>(code) + ")") {
|
||||
}
|
||||
};
|
||||
|
||||
static void scripting_engine_panic_handler(duk_errcode_t code, const char* msg)
|
||||
{
|
||||
throw ScriptingEngineException(code, msg);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#include <duktape.c>
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 <duktape.h>
|
||||
|
||||
class ScriptingEngineException : public base::Exception {
|
||||
public:
|
||||
ScriptingEngineException(duk_errcode_t code, const char* msg)
|
||||
: Exception(std::string(msg) + " (code " + base::convert_to<std::string>(code) + ")") {
|
||||
}
|
||||
};
|
||||
|
||||
static void scripting_engine_panic_handler(duk_errcode_t code, const char* msg)
|
||||
{
|
||||
throw ScriptingEngineException(code, msg);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#include <duktape.c>
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user