Change scripting language to Lua

This commit is contained in:
David Capello 2018-08-20 11:25:08 -03:00
parent 70629d6f89
commit 4fe66f2ffb
45 changed files with 1178 additions and 1747 deletions

6
.gitmodules vendored
View File

@ -51,9 +51,6 @@
[submodule "third_party/benchmark"] [submodule "third_party/benchmark"]
path = third_party/benchmark path = third_party/benchmark
url = https://github.com/aseprite/benchmark.git url = https://github.com/aseprite/benchmark.git
[submodule "third_party/mujs"]
path = third_party/mujs
url = https://github.com/aseprite/mujs.git
[submodule "third_party/giflib"] [submodule "third_party/giflib"]
path = third_party/giflib path = third_party/giflib
url = https://github.com/aseprite/giflib.git url = https://github.com/aseprite/giflib.git
@ -63,3 +60,6 @@
[submodule "third_party/tinyexpr"] [submodule "third_party/tinyexpr"]
path = third_party/tinyexpr path = third_party/tinyexpr
url = https://github.com/aseprite/tinyexpr.git url = https://github.com/aseprite/tinyexpr.git
[submodule "third_party/lua"]
path = third_party/lua
url = https://github.com/aseprite/lua

View File

@ -668,7 +668,7 @@
<item command="PasteText" text="@.edit_insert_text" /> <item command="PasteText" text="@.edit_insert_text" />
<!--menu text="Scripts"> <!--menu text="Scripts">
<item command="RunScript" text="Transparency from White Background"> <item command="RunScript" text="Transparency from White Background">
<param name="filename" value="white_to_alpha.js" /> <param name="filename" value="white_to_alpha.lua" />
</item> </item>
</menu--> </menu-->
<separator /> <separator />

View File

@ -1,20 +0,0 @@
// Aseprite
// Copyright (C) 2015-2017 by David Capello
var col = app.pixelColor
var img = app.activeImage
for (var y=0; y<img.height; ++y) {
for (var x=0; x<img.width; ++x) {
var c = img.getPixel(x, y)
var v = (col.rgbaR(c)+
col.rgbaG(c)+
col.rgbaB(c))/3
img.putPixel(x, y,
col.rgba(col.rgbaR(c),
col.rgbaG(c),
col.rgbaB(c),
255-v))
}
}

View File

@ -0,0 +1,17 @@
-- Aseprite
-- Copyright (C) 2015-2018 by David Capello
local pc = app.pixelColor
local img = app.activeImage
for y=0,img.height-1 do
for x=0,img.width-1 do
local c = img:getPixel(x, y)
local r = pc.rgbaR(c)
local g = pc.rgbaG(c)
local b = pc.rgbaB(c)
local a = pc.rgbaA(c)
if a > 0 then a = 255 - (r+g+b)/3 end
img:putPixel(x, y, pc.rgba(r, g, b, a))
end
end

View File

@ -904,24 +904,29 @@ freely, subject to the following restrictions:
distribution. distribution.
``` ```
# [mujs](http://mujs.com/) # [Lua](https://www.lua.org/)
``` ```
ISC License Copyright (C) 1994-2018 Lua.org, PUC-Rio.
Copyright (c) 2013, 2017, Artifex Software 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:
Permission to use, copy, modify, and/or distribute this software for any The above copyright notice and this permission notice shall be
purpose with or without fee is hereby granted, provided that the above included in all copies or substantial portions of the Software.
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
PERFORMANCE OF THIS SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
``` ```
# [pixman](http://www.pixman.org/) # [pixman](http://www.pixman.org/)

View File

@ -115,11 +115,6 @@ add_subdirectory(ft)
add_subdirectory(she) add_subdirectory(she)
add_subdirectory(ui) add_subdirectory(ui)
if(ENABLE_SCRIPTING)
add_subdirectory(script)
add_definitions(-DENABLE_SCRIPTING)
endif()
if(ENABLE_UPDATER) if(ENABLE_UPDATER)
add_subdirectory(updater) add_subdirectory(updater)
endif() endif()

View File

@ -140,6 +140,7 @@ endif()
set(scripting_files) set(scripting_files)
if(ENABLE_SCRIPTING) if(ENABLE_SCRIPTING)
add_definitions(-DENABLE_SCRIPTING)
set(scripting_files_ui) set(scripting_files_ui)
if(ENABLE_UI) if(ENABLE_UI)
set(scripting_files_ui set(scripting_files_ui
@ -149,10 +150,10 @@ if(ENABLE_SCRIPTING)
set(scripting_files set(scripting_files
commands/cmd_run_script.cpp commands/cmd_run_script.cpp
script/app_object.cpp script/app_object.cpp
script/app_scripting.cpp script/engine.cpp
script/console_object.cpp
script/image_class.cpp script/image_class.cpp
script/pixel_color_class.cpp script/luacpp.cpp
script/pixel_color_object.cpp
script/point_class.cpp script/point_class.cpp
script/rectangle_class.cpp script/rectangle_class.cpp
script/selection_class.cpp script/selection_class.cpp
@ -595,7 +596,7 @@ target_link_libraries(app-lib
tinyexpr) tinyexpr)
if(ENABLE_SCRIPTING) if(ENABLE_SCRIPTING)
target_link_libraries(app-lib script-lib) target_link_libraries(app-lib lua lauxlib lualib)
endif() endif()
if(ENABLE_UPDATER) if(ENABLE_UPDATER)

View File

@ -70,9 +70,8 @@
#include <memory> #include <memory>
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "app/shell.h" #include "app/shell.h"
#include "script/engine_delegate.h"
#endif #endif
#ifdef ENABLE_STEAM #ifdef ENABLE_STEAM
@ -353,7 +352,7 @@ void App::run()
// Start shell to execute scripts. // Start shell to execute scripts.
if (m_isShell) { if (m_isShell) {
script::StdoutEngineDelegate delegate; script::StdoutEngineDelegate delegate;
AppScripting engine(&delegate); script::Engine engine(&delegate);
engine.printLastResult(); engine.printLastResult();
Shell shell; Shell shell;
shell.run(engine); shell.run(engine);

View File

@ -27,8 +27,7 @@
#include "doc/sprite.h" #include "doc/sprite.h"
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "script/engine_delegate.h"
#endif #endif
#include <iostream> #include <iostream>
@ -128,7 +127,7 @@ void DefaultCliDelegate::execScript(const std::string& filename)
{ {
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
script::StdoutEngineDelegate delegate; script::StdoutEngineDelegate delegate;
AppScripting engine(&delegate); script::Engine engine(&delegate);
if (!engine.evalFile(filename)) if (!engine.evalFile(filename))
throw std::runtime_error("Error executing script"); throw std::runtime_error("Error executing script");
#endif #endif

View File

@ -16,9 +16,8 @@
#include "app/commands/params.h" #include "app/commands/params.h"
#include "app/console.h" #include "app/console.h"
#include "app/resource_finder.h" #include "app/resource_finder.h"
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "base/fs.h" #include "base/fs.h"
#include "script/engine_delegate.h"
#include "ui/manager.h" #include "ui/manager.h"
#include <cstdio> #include <cstdio>
@ -67,7 +66,7 @@ void RunScriptCommand::onLoadParams(const Params& params)
void RunScriptCommand::onExecute(Context* context) void RunScriptCommand::onExecute(Context* context)
{ {
ConsoleEngineDelegate delegate; ConsoleEngineDelegate delegate;
AppScripting engine(&delegate); script::Engine engine(&delegate);
engine.evalFile(m_filename); engine.evalFile(m_filename);
ui::Manager::getDefault()->invalidate(); ui::Manager::getDefault()->invalidate();

View File

@ -13,154 +13,155 @@
#include "app/commands/params.h" #include "app/commands/params.h"
#include "app/context.h" #include "app/context.h"
#include "app/doc.h" #include "app/doc.h"
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/site.h" #include "app/site.h"
#include "app/site.h" #include "app/site.h"
#include "app/tx.h" #include "app/tx.h"
#include "script/engine.h"
#include <iostream> #include <iostream>
namespace app { namespace app {
namespace script {
namespace { namespace {
void App_open(script::ContextHandle handle) int App_open(lua_State* L)
{ {
script::Context ctx(handle); const char* filename = luaL_checkstring(L, 1);
const char* filename = ctx.requireString(1);
app::Context* appCtx = App::instance()->context(); app::Context* ctx = App::instance()->context();
Doc* oldDoc = appCtx->activeDocument(); Doc* oldDoc = ctx->activeDocument();
Command* openCommand = Command* openCommand =
Commands::instance()->byId(CommandId::OpenFile()); Commands::instance()->byId(CommandId::OpenFile());
Params params; Params params;
params.set("filename", filename); params.set("filename", filename);
appCtx->executeCommand(openCommand, params); ctx->executeCommand(openCommand, params);
Doc* newDoc = appCtx->activeDocument(); Doc* newDoc = ctx->activeDocument();
if (newDoc != oldDoc) if (newDoc != oldDoc)
push_sprite(ctx, newDoc->sprite()); push_ptr(L, newDoc->sprite());
else else
ctx.pushNull(); lua_pushnil(L);
return 1;
} }
void App_exit(script::ContextHandle handle) int App_exit(lua_State* L)
{ {
script::Context ctx(handle); app::Context* ctx = App::instance()->context();
app::Context* appCtx = App::instance()->context(); if (ctx && ctx->isUIAvailable()) {
if (appCtx && appCtx->isUIAvailable()) {
Command* exitCommand = Command* exitCommand =
Commands::instance()->byId(CommandId::Exit()); Commands::instance()->byId(CommandId::Exit());
appCtx->executeCommand(exitCommand); ctx->executeCommand(exitCommand);
} }
ctx.pushUndefined(); return 0;
} }
void App_transaction(script::ContextHandle handle) int App_transaction(lua_State* L)
{ {
script::Context ctx(handle); int top = lua_gettop(L);
if (ctx.isCallable(1)) { int nresults = 0;
if (lua_isfunction(L, 1)) {
Tx tx; // Create a new transaction so it exists in the whole Tx tx; // Create a new transaction so it exists in the whole
// duration of the argument function call. // duration of the argument function call.
ctx.copy(1); lua_pushvalue(L, -1);
ctx.call(0); if (lua_pcall(L, 0, LUA_MULTRET, 0) == LUA_OK)
tx.commit(); tx.commit();
nresults = lua_gettop(L) - top;
} }
else return nresults;
ctx.pushUndefined();
} }
void App_undo(script::ContextHandle handle) int App_undo(lua_State* L)
{ {
script::Context ctx(handle); app::Context* ctx = App::instance()->context();
app::Context* appCtx = App::instance()->context(); if (ctx) {
if (appCtx) {
Command* undo = Commands::instance()->byId(CommandId::Undo()); Command* undo = Commands::instance()->byId(CommandId::Undo());
appCtx->executeCommand(undo); ctx->executeCommand(undo);
} }
ctx.pushUndefined(); return 0;
} }
void App_redo(script::ContextHandle handle) int App_redo(lua_State* L)
{ {
script::Context ctx(handle); app::Context* ctx = App::instance()->context();
app::Context* appCtx = App::instance()->context(); if (ctx) {
if (appCtx) {
Command* redo = Commands::instance()->byId(CommandId::Redo()); Command* redo = Commands::instance()->byId(CommandId::Redo());
appCtx->executeCommand(redo); ctx->executeCommand(redo);
} }
ctx.pushUndefined(); return 0;
} }
void App_get_activeSprite(script::ContextHandle handle) int App_get_activeSprite(lua_State* L)
{ {
script::Context ctx(handle); app::Context* ctx = App::instance()->context();
app::Context* appCtx = App::instance()->context(); Doc* doc = ctx->activeDocument();
Doc* doc = appCtx->activeDocument();
if (doc) if (doc)
push_sprite(ctx, doc->sprite()); push_ptr(L, doc->sprite());
else else
ctx.pushNull(); lua_pushnil(L);
return 1;
} }
void App_get_activeImage(script::ContextHandle handle) int App_get_activeImage(lua_State* L)
{ {
script::Context ctx(handle); app::Context* ctx = App::instance()->context();
app::Context* appCtx = App::instance()->context(); Site site = ctx->activeSite();
Site site = appCtx->activeSite();
if (site.image()) if (site.image())
push_image(ctx, site.image()); push_ptr(L, site.image());
else else
ctx.pushNull(); lua_pushnil(L);
return 1;
} }
void App_get_site(script::ContextHandle handle) int App_get_site(lua_State* L)
{ {
script::Context ctx(handle); app::Context* ctx = App::instance()->context();
app::Context* appCtx = App::instance()->context(); Site site = ctx->activeSite();
Site site = appCtx->activeSite(); push_obj(L, site);
push_site(ctx, site); return 1;
} }
void App_get_pixelColor(script::ContextHandle handle) int App_get_version(lua_State* L)
{ {
script::Context ctx(handle); lua_pushstring(L, VERSION);
ctx.newObject("PixelColor", nullptr, nullptr); return 1;
} }
void App_get_version(script::ContextHandle handle) const luaL_Reg App_methods[] = {
{ { "open", App_open },
script::Context ctx(handle); { "exit", App_exit },
ctx.pushString(VERSION); { "transaction", App_transaction },
} { "undo", App_undo },
{ "redo", App_redo },
const script::FunctionEntry App_methods[] = { { nullptr, nullptr }
{ "open", App_open, 1 },
{ "exit", App_exit, 0 },
{ "transaction", App_transaction, 1 },
{ "undo", App_undo, 0 },
{ "redo", App_redo, 0 },
{ nullptr, nullptr, 0 }
}; };
const script::PropertyEntry App_props[] = { const Property App_properties[] = {
{ "activeSprite", App_get_activeSprite, nullptr }, { "activeSprite", App_get_activeSprite, nullptr },
{ "activeImage", App_get_activeImage, nullptr }, { "activeImage", App_get_activeImage, nullptr },
{ "pixelColor", App_get_pixelColor, nullptr },
{ "version", App_get_version, nullptr }, { "version", App_get_version, nullptr },
{ "site", App_get_site, nullptr }, { "site", App_get_site, nullptr },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_app_object(script::Context& ctx) DEF_MTNAME(App);
void register_app_object(lua_State* L)
{ {
ctx.pushGlobalObject(); REG_CLASS(L, App);
ctx.registerObject(-1, "app", App_methods, App_props); REG_CLASS_PROPERTIES(L, App);
ctx.pop();
lua_newtable(L); // Create a table which will be the "app" object
lua_pushvalue(L, -1);
luaL_getmetatable(L, get_mtname<App>());
lua_setmetatable(L, -2);
lua_setglobal(L, "app");
lua_pop(L, 1); // Pop app table
} }
} // namespace script
} // namespace app } // namespace app

View File

@ -1,78 +0,0 @@
// Aseprite
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/script/app_scripting.h"
#include "app/doc.h"
namespace app {
namespace {
const script::ConstantEntry ColorMode_constants[] = {
{ "RGB", double(IMAGE_RGB) },
{ "GRAYSCALE", double(IMAGE_GRAYSCALE) },
{ "INDEXED", double(IMAGE_INDEXED) },
{ nullptr, 0.0 }
};
}
void register_app_object(script::Context& ctx);
void register_console_object(script::Context& ctx);
void register_image_class(script::index_t idx, script::Context& ctx);
void register_pixel_color_class(script::index_t idx, script::Context& ctx);
void register_point_class(script::index_t idx, script::Context& ctx);
void register_rectangle_class(script::index_t idx, script::Context& ctx);
void register_selection_class(script::index_t idx, script::Context& ctx);
void register_site_class(script::index_t idx, script::Context& ctx);
void register_size_class(script::index_t idx, script::Context& ctx);
void register_sprite_class(script::index_t idx, script::Context& ctx);
AppScripting::AppScripting(script::EngineDelegate* delegate)
: script::Engine(delegate)
{
auto& ctx = context();
ctx.setContextUserData(this);
// Register global objects (app and console)
register_app_object(ctx);
register_console_object(ctx);
ctx.pushGlobalObject();
// Register constants
{
ctx.newObject();
ctx.registerConstants(-1, ColorMode_constants);
ctx.setProp(-2, "ColorMode");
}
// Register classes/prototypes
register_image_class(-1, ctx);
register_pixel_color_class(-1, ctx);
register_point_class(-1, ctx);
register_rectangle_class(-1, ctx);
register_selection_class(-1, ctx);
register_site_class(-1, ctx);
register_size_class(-1, ctx);
register_sprite_class(-1, ctx);
ctx.pop(1);
}
AppScripting* get_engine(script::Context& ctx)
{
return (AppScripting*)ctx.getContextUserData();
}
}

View File

@ -1,50 +0,0 @@
// Aseprite
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_SCRIPTING_H_INCLUDED
#define APP_SCRIPTING_H_INCLUDED
#pragma once
#ifndef ENABLE_SCRIPTING
#error ENABLE_SCRIPTING must be defined
#endif
#include "doc/object_id.h"
#include "gfx/fwd.h"
#include "script/engine.h"
#include <map>
namespace doc {
class Image;
class Sprite;
}
namespace app {
class Site;
class AppScripting : public script::Engine {
public:
AppScripting(script::EngineDelegate* delegate);
};
AppScripting* get_engine(script::Context& ctx);
void push_image(script::Context& ctx, doc::Image* image);
void push_new_point(script::Context& ctx, const gfx::Point& pt);
void push_new_rectangle(script::Context& ctx, const gfx::Rect& rc);
void push_new_size(script::Context& ctx, const gfx::Size& rc);
void push_site(script::Context& ctx, app::Site& site);
void push_sprite(script::Context& ctx, doc::Sprite* sprite);
void push_sprite_selection(script::Context& ctx, doc::Sprite* sprite);
gfx::Point convert_args_into_point(script::Context& ctx);
gfx::Rect convert_args_into_rectangle(script::Context& ctx);
gfx::Size convert_args_into_size(script::Context& ctx);
} // namespace app
#endif

View File

@ -1,74 +0,0 @@
// Aseprite
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/app.h"
#include "app/console.h"
#include "script/engine.h"
#include <iostream>
namespace app {
namespace {
void print(const char* str)
{
if (str) {
std::cout << str << std::endl; // New line + flush
if (App::instance()->isGui()) {
Console().printf("%s\n", str);
}
}
}
void Console_assert(script::ContextHandle handle)
{
script::Context ctx(handle);
if (!ctx.toBool(1))
ctx.error(ctx.toString(1));
ctx.pushUndefined();
}
void Console_log(script::ContextHandle handle)
{
script::Context ctx(handle);
std::string output;
int top = ctx.top();
const char* s;
for (int n=1; n<top; ++n) {
s = ctx.toString(n);
if (s == nullptr)
break;
if (n > 1)
output += " ";
output += s;
}
if (!output.empty())
print(output.c_str());
ctx.pushUndefined();
}
const script::FunctionEntry Console_methods[] = {
{ "assert", Console_assert, 2 },
{ "log", Console_log, 1 },
{ nullptr, nullptr, 0 }
};
} // anonymous namespace
void register_console_object(script::Context& ctx)
{
ctx.pushGlobalObject();
ctx.registerObject(-1, "console", Console_methods, nullptr);
ctx.pop();
}
} // namespace app

162
src/app/script/engine.cpp Normal file
View File

@ -0,0 +1,162 @@
// Aseprite
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/script/engine.h"
#include "app/app.h"
#include "app/console.h"
#include "app/script/luacpp.h"
#include "base/fstream_path.h"
#include "doc/color_mode.h"
#include <fstream>
#include <sstream>
namespace app {
namespace script {
namespace {
int print(lua_State* L)
{
std::string output;
int n = lua_gettop(L); /* number of arguments */
int i;
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
lua_pushvalue(L, -1); // function to be called
lua_pushvalue(L, i); // value to print
lua_call(L, 1, 1);
size_t l;
const char* s = lua_tolstring(L, -1, &l); // get result
if (s == nullptr)
return luaL_error(L, "'tostring' must return a string to 'print'");
if (i > 1)
output.push_back('\t');
output.insert(output.size(), s, l);
lua_pop(L, 1); // pop result
}
if (!output.empty()) {
std::printf("%s\n", output.c_str());
std::fflush(stdout);
if (App::instance()->isGui())
Console().printf("%s\n", output.c_str());
}
return 0;
}
} // anonymous namespace
void register_app_object(lua_State* L);
void register_app_pixel_color_object(lua_State* L);
void register_image_class(lua_State* L);
void register_point_class(lua_State* L);
void register_rect_class(lua_State* L);
void register_selection_class(lua_State* L);
void register_site_class(lua_State* L);
void register_size_class(lua_State* L);
void register_sprite_class(lua_State* L);
Engine::Engine(EngineDelegate* delegate)
: L(luaL_newstate())
, m_delegate(delegate)
, m_printLastResult(false)
{
int top = lua_gettop(L);
// Standard Lua libraries
luaL_requiref(L, LUA_GNAME, luaopen_base, 1);
luaL_requiref(L, LUA_MATHLIBNAME, luaopen_math, 1);
luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1);
luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 1);
lua_pop(L, 4);
// Our print() impl
lua_register(L, "print", print);
// Generic code used by metatables
run_mt_index_code(L);
// Register global app object
register_app_object(L);
register_app_pixel_color_object(L);
// Register constants
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setglobal(L, "ColorMode");
setfield_integer(L, "RGB", doc::ColorMode::RGB);
setfield_integer(L, "GRAYSCALE", doc::ColorMode::GRAYSCALE);
setfield_integer(L, "INDEXED", doc::ColorMode::INDEXED);
lua_pop(L, 1);
// Register classes/prototypes
register_image_class(L);
register_point_class(L);
register_rect_class(L);
register_selection_class(L);
register_site_class(L);
register_size_class(L);
register_sprite_class(L);
// Check that we have a clean start (without dirty in the stack)
ASSERT(lua_gettop(L) == top);
}
Engine::~Engine()
{
lua_close(L);
}
void Engine::printLastResult()
{
m_printLastResult = true;
}
bool Engine::evalCode(const std::string& code,
const std::string& filename)
{
bool ok = true;
if (luaL_loadbuffer(L, code.c_str(), code.size(), filename.c_str()) ||
lua_pcall(L, 0, 1, 0)) {
// Error case
std::string err;
const char* s = lua_tostring(L, -1);
if (s)
m_delegate->onConsolePrint(s);
ok = false;
}
else {
// Code was executed correctly
if (m_printLastResult) {
if (!lua_isnone(L, -1)) {
const char* result = lua_tostring(L, -1);
if (result)
m_delegate->onConsolePrint(result);
}
}
}
lua_pop(L, 1);
return ok;
}
bool Engine::evalFile(const std::string& filename)
{
std::stringstream buf;
{
std::ifstream s(FSTREAM_PATH(filename));
buf << s.rdbuf();
}
return evalCode(buf.str(), filename);
}
} // namespace script
} // namespace app

72
src/app/script/engine.h Normal file
View File

@ -0,0 +1,72 @@
// Aseprite
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_SCRIPTING_H_INCLUDED
#define APP_SCRIPTING_H_INCLUDED
#pragma once
#ifndef ENABLE_SCRIPTING
#error ENABLE_SCRIPTING must be defined
#endif
#include "doc/object_id.h"
#include "gfx/fwd.h"
#include <cstdio>
#include <string>
#include <map>
struct lua_State;
namespace doc {
class Image;
class Sprite;
}
namespace app {
class Site;
namespace script {
class EngineDelegate {
public:
virtual ~EngineDelegate() { }
virtual void onConsolePrint(const char* text) = 0;
};
class StdoutEngineDelegate : public EngineDelegate {
public:
void onConsolePrint(const char* text) override {
std::printf("%s\n", text);
}
};
class Engine {
public:
Engine(EngineDelegate* delegate);
~Engine();
void printLastResult();
bool evalCode(const std::string& code,
const std::string& filename = std::string());
bool evalFile(const std::string& filename);
private:
lua_State* L;
EngineDelegate* m_delegate;
bool m_printLastResult;
};
void push_sprite_selection(lua_State* L, doc::Sprite* sprite);
gfx::Point convert_args_into_point(lua_State* L, int index);
gfx::Rect convert_args_into_rect(lua_State* L, int index);
gfx::Size convert_args_into_size(lua_State* L, int index);
} // namespace script
} // namespace app
#endif

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2015-2017 David Capello // Copyright (C) 2015-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -8,95 +8,78 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/script/luacpp.h"
#include "doc/image.h" #include "doc/image.h"
#include "script/engine.h" #include "doc/primitives.h"
namespace app { namespace app {
namespace script {
namespace { namespace {
const char* kTag = "Image"; int Image_new(lua_State* L)
void Image_new(script::ContextHandle handle)
{ {
script::Context ctx(handle); lua_pushnil(L); // TODO
ctx.pushNull(); // TODO return 1;
} }
void Image_putPixel(script::ContextHandle handle) int Image_putPixel(lua_State* L)
{ {
script::Context ctx(handle); auto image = get_ptr<doc::Image>(L, 1);
auto image = (doc::Image*)ctx.toUserData(0, kTag); const int x = lua_tointeger(L, 2);
int x = ctx.requireInt(1); const int y = lua_tointeger(L, 3);
int y = ctx.requireInt(2); const doc::color_t color = lua_tointeger(L, 4);
doc::color_t color = ctx.requireUInt(3); doc::put_pixel(image, x, y, color);
return 0;
if (image) {
image->putPixel(x, y, color);
}
ctx.pushUndefined();
} }
void Image_getPixel(script::ContextHandle handle) int Image_getPixel(lua_State* L)
{ {
script::Context ctx(handle); const auto image = get_ptr<doc::Image>(L, 1);
auto image = (doc::Image*)ctx.toUserData(0, kTag); const int x = lua_tointeger(L, 2);
int x = ctx.requireInt(1); const int y = lua_tointeger(L, 3);
int y = ctx.requireInt(2); const doc::color_t color = doc::get_pixel(image, x, y);
lua_pushinteger(L, color);
if (image) { return 1;
doc::color_t color = image->getPixel(x, y);
ctx.pushUInt(color);
}
else
ctx.pushUndefined();
} }
void Image_get_width(script::ContextHandle handle) int Image_get_width(lua_State* L)
{ {
script::Context ctx(handle); const auto image = get_ptr<doc::Image>(L, 1);
auto image = (doc::Image*)ctx.toUserData(0, kTag); lua_pushinteger(L, image->width());
if (image) return 1;
ctx.pushInt(image->width());
else
ctx.pushUndefined();
} }
void Image_get_height(script::ContextHandle handle) int Image_get_height(lua_State* L)
{ {
script::Context ctx(handle); const auto image = get_ptr<doc::Image>(L, 1);
auto image = (doc::Image*)ctx.toUserData(0, kTag); lua_pushinteger(L, image->height());
if (image) return 1;
ctx.pushInt(image->height());
else
ctx.pushUndefined();
} }
const script::FunctionEntry Image_methods[] = { const luaL_Reg Image_methods[] = {
{ "getPixel", Image_getPixel, 2 }, { "getPixel", Image_getPixel },
{ "putPixel", Image_putPixel, 3 }, { "putPixel", Image_putPixel },
{ nullptr, nullptr, 0 } { nullptr, nullptr }
}; };
const script::PropertyEntry Image_props[] = { const Property Image_properties[] = {
{ "width", Image_get_width, nullptr }, { "width", Image_get_width, nullptr },
{ "height", Image_get_height, nullptr }, { "height", Image_get_height, nullptr },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_image_class(script::index_t idx, script::Context& ctx) DEF_MTNAME(doc::Image);
void register_image_class(lua_State* L)
{ {
ctx.registerClass(idx, kTag, using doc::Image;
Image_new, 0, REG_CLASS(L, Image);
Image_methods, Image_props); REG_CLASS_NEW(L, Image);
} REG_CLASS_PROPERTIES(L, Image);
void push_image(script::Context& ctx, doc::Image* image)
{
ctx.newObject(kTag, image, nullptr);
} }
} // namespace script
} // namespace app } // namespace app

86
src/app/script/luacpp.cpp Normal file
View File

@ -0,0 +1,86 @@
// Aseprite
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/script/luacpp.h"
namespace app {
namespace script {
static const char* mt_index_code =
"__generic_mt_index = function(t, k) "
" local mt = getmetatable(t) "
" local f = mt[k] "
" if f then return f end "
" f = mt.__getters[k] "
" if f then return f(t) end "
" if type(t) == 'table' then return rawget(t, k) end "
" error('Field '..tostring(k)..' does not exist')"
"end "
"__generic_mt_newindex = function(t, k, v) "
" local mt = getmetatable(t) "
" local f = mt[k] "
" if f then return f end "
" f = mt.__setters[k] "
" if f then return f(t, v) end "
" if type(t) == 'table' then return rawset(t, k, v) end "
" error('Cannot set field '..tostring(k))"
"end";
void run_mt_index_code(lua_State* L)
{
luaL_dostring(L, mt_index_code);
}
void create_mt_getters_setters(lua_State* L,
const char* tname,
const Property* properties)
{
const int top = lua_gettop(L);
bool withGetters = false;
bool withSetters = false;
for (auto p=properties; p->name; ++p) {
if (p->getter) withGetters = true;
if (p->setter) withSetters = true;
}
ASSERT(withGetters || withSetters);
luaL_getmetatable(L, tname);
if (withGetters) {
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setfield(L, -3, "__getters");
for (auto p=properties; p->name; ++p) {
if (p->getter) {
lua_pushcclosure(L, p->getter, 0);
lua_setfield(L, -2, p->name);
}
}
lua_pop(L, 1);
}
if (withSetters) {
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setfield(L, -3, "__setters");
for (auto p=properties; p->name; ++p) {
if (p->setter) {
lua_pushcclosure(L, p->setter, 0);
lua_setfield(L, -2, p->name);
}
}
lua_pop(L, 1);
}
lua_pop(L, 1);
ASSERT(lua_gettop(L) == top);
}
} // namespace script
} // namespace app

118
src/app/script/luacpp.h Normal file
View File

@ -0,0 +1,118 @@
// Aseprite
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_SCRIPT_LUACPP_H_INCLUDED
#define APP_SCRIPT_LUACPP_H_INCLUDED
#pragma once
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
#include <functional>
#include <type_traits>
namespace app {
namespace script {
// Some of these auxiliary methods are based on code from the Skia
// library (SkLua.cpp file) by Google Inc.
template <typename T> const char* get_mtname();
#define DEF_MTNAME(T) \
template <> const char* get_mtname<T>() { \
return #T "_Metatable"; \
}
template <typename T, typename... Args> T* push_new(lua_State* L, Args&&... args) {
T* addr = (T*)lua_newuserdata(L, sizeof(T));
new (addr) T(std::forward<Args>(args)...);
luaL_getmetatable(L, get_mtname<T>());
lua_setmetatable(L, -2);
return addr;
}
template <typename T> void push_obj(lua_State* L, const T& obj) {
new (lua_newuserdata(L, sizeof(T))) T(obj);
luaL_getmetatable(L, get_mtname<T>());
lua_setmetatable(L, -2);
}
template <typename T> void push_obj(lua_State* L, T&& obj) {
using RRT = typename std::remove_reference<T>::type;
new (lua_newuserdata(L, sizeof(RRT))) RRT(std::move(obj));
luaL_getmetatable(L, get_mtname<RRT>());
lua_setmetatable(L, -2);
}
template <typename T> T* push_ptr(lua_State* L, T* ptr) {
*(T**)lua_newuserdata(L, sizeof(T*)) = ptr;
luaL_getmetatable(L, get_mtname<T>());
lua_setmetatable(L, -2);
return ptr;
}
template <typename T> T* get_ptr(lua_State* L, int index) {
return *(T**)luaL_checkudata(L, index, get_mtname<T>());
}
template <typename T> T* get_obj(lua_State* L, int index) {
return (T*)luaL_checkudata(L, index, get_mtname<T>());
}
// Returns nil if the index doesn't have the given metatable
template <typename T> T* may_get_obj(lua_State* L, int index) {
return (T*)luaL_testudata(L, index, get_mtname<T>());
}
inline bool lua2bool(lua_State* L, int index) {
return !!lua_toboolean(L, index);
}
template<typename T>
inline void setfield_integer(lua_State* L, const char* key, const T& value) {
lua_pushinteger(L, int(value));
lua_setfield(L, -2, key);
}
#define REG_CLASS(L, T) { \
luaL_newmetatable(L, get_mtname<T>()); \
lua_getglobal(L, "__generic_mt_index"); \
lua_setfield(L, -2, "__index"); \
lua_getglobal(L, "__generic_mt_newindex"); \
lua_setfield(L, -2, "__newindex"); \
luaL_setfuncs(L, T##_methods, 0); \
lua_pop(L, 1); \
}
#define REG_CLASS_NEW(L, T) { \
lua_pushcfunction(L, T##_new); \
lua_setglobal(L, #T); \
}
struct Property {
const char* name;
lua_CFunction getter;
lua_CFunction setter;
};
void run_mt_index_code(lua_State* L);
void create_mt_getters_setters(lua_State* L,
const char* tname,
const Property* properties);
#define REG_CLASS_PROPERTIES(L, T) { \
luaL_getmetatable(L, get_mtname<T>()); \
create_mt_getters_setters(L, get_mtname<T>(), T##_properties); \
lua_pop(L, 1); \
}
} // namespace script
} // namespace app
#endif

View File

@ -1,101 +0,0 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "doc/color.h"
#include "script/engine.h"
namespace app {
namespace {
const char* kTag = "PixelColor";
void PixelColor_new(script::ContextHandle handle)
{
script::Context ctx(handle);
ctx.pushUndefined(); // Cannot create new pixel colors (just subobject of "app")
}
void PixelColor_rgba(script::ContextHandle handle)
{
script::Context ctx(handle);
const int r = ctx.requireUInt(1);
const int g = ctx.requireUInt(2);
const int b = ctx.requireUInt(3);
const int a = (ctx.isUndefined(4) ? 255: ctx.requireUInt(4));
ctx.pushUInt(doc::rgba(r, g, b, a));
}
void PixelColor_rgbaR(script::ContextHandle handle)
{
script::Context ctx(handle);
ctx.pushUInt(doc::rgba_getr(ctx.requireUInt(1)));
}
void PixelColor_rgbaG(script::ContextHandle handle)
{
script::Context ctx(handle);
ctx.pushUInt(doc::rgba_getg(ctx.requireUInt(1)));
}
void PixelColor_rgbaB(script::ContextHandle handle)
{
script::Context ctx(handle);
ctx.pushUInt(doc::rgba_getb(ctx.requireUInt(1)));
}
void PixelColor_rgbaA(script::ContextHandle handle)
{
script::Context ctx(handle);
ctx.pushUInt(doc::rgba_geta(ctx.requireUInt(1)));
}
void PixelColor_graya(script::ContextHandle handle)
{
script::Context ctx(handle);
int v = ctx.requireUInt(1);
int a = (ctx.isUndefined(2) ? 255: ctx.requireUInt(2));
ctx.pushUInt(doc::graya(v, a));
}
void PixelColor_grayaV(script::ContextHandle handle)
{
script::Context ctx(handle);
ctx.pushUInt(doc::graya_getv(ctx.requireUInt(1)));
}
void PixelColor_grayaA(script::ContextHandle handle)
{
script::Context ctx(handle);
ctx.pushUInt(doc::graya_geta(ctx.requireUInt(1)));
}
const script::FunctionEntry PixelColor_methods[] = {
{ "rgba", PixelColor_rgba, 4 },
{ "rgbaR", PixelColor_rgbaR, 1 },
{ "rgbaG", PixelColor_rgbaG, 1 },
{ "rgbaB", PixelColor_rgbaB, 1 },
{ "rgbaA", PixelColor_rgbaA, 1 },
{ "graya", PixelColor_graya, 2 },
{ "grayaV", PixelColor_grayaV, 1 },
{ "grayaA", PixelColor_grayaA, 1 },
{ nullptr, nullptr, 0 }
};
} // anonymous namespace
void register_pixel_color_class(script::index_t idx, script::Context& ctx)
{
ctx.registerClass(idx, kTag,
PixelColor_new, 0,
PixelColor_methods, nullptr);
}
} // namespace app

View File

@ -0,0 +1,99 @@
// Aseprite
// Copyright (C) 2017-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/script/luacpp.h"
#include "doc/color.h"
namespace app {
namespace script {
namespace {
int PixelColor_rgba(lua_State* L)
{
const int r = lua_tointeger(L, 1);
const int g = lua_tointeger(L, 2);
const int b = lua_tointeger(L, 3);
const int a = (lua_isnoneornil(L, 4) ? 255: lua_tointeger(L, 4));
lua_pushinteger(L, doc::rgba(r, g, b, a));
return 1;
}
int PixelColor_rgbaR(lua_State* L)
{
lua_pushinteger(L, doc::rgba_getr(lua_tointeger(L, 1)));
return 1;
}
int PixelColor_rgbaG(lua_State* L)
{
lua_pushinteger(L, doc::rgba_getg(lua_tointeger(L, 1)));
return 1;
}
int PixelColor_rgbaB(lua_State* L)
{
lua_pushinteger(L, doc::rgba_getb(lua_tointeger(L, 1)));
return 1;
}
int PixelColor_rgbaA(lua_State* L)
{
lua_pushinteger(L, doc::rgba_geta(lua_tointeger(L, 1)));
return 1;
}
int PixelColor_graya(lua_State* L)
{
int v = lua_tointeger(L, 1);
int a = (lua_isnoneornil(L, 2) ? 255: lua_tointeger(L, 2));
lua_pushinteger(L, doc::graya(v, a));
return 1;
}
int PixelColor_grayaV(lua_State* L)
{
lua_pushinteger(L, doc::graya_getv(lua_tointeger(L, 1)));
return 1;
}
int PixelColor_grayaA(lua_State* L)
{
lua_pushinteger(L, doc::graya_geta(lua_tointeger(L, 1)));
return 1;
}
const luaL_Reg PixelColor_methods[] = {
{ "rgba", PixelColor_rgba },
{ "rgbaR", PixelColor_rgbaR },
{ "rgbaG", PixelColor_rgbaG },
{ "rgbaB", PixelColor_rgbaB },
{ "rgbaA", PixelColor_rgbaA },
{ "graya", PixelColor_graya },
{ "grayaV", PixelColor_grayaV },
{ "grayaA", PixelColor_grayaA },
{ nullptr, nullptr }
};
} // anonymous namespace
void register_app_pixel_color_object(lua_State* L)
{
lua_getglobal(L, "app");
lua_newtable(L); // New table for pixelColor
lua_pushstring(L, "pixelColor");
lua_pushvalue(L, -2); // Copy table
lua_rawset(L, -4);
luaL_setfuncs(L, PixelColor_methods, 0);
lua_pop(L, 2); // Pop table & app global
}
} // namespace script
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2017 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -8,111 +8,96 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/script/luacpp.h"
#include "gfx/point.h" #include "gfx/point.h"
#include "script/engine.h"
namespace app { namespace app {
namespace script {
namespace { namespace {
const char* kTag = "Point"; gfx::Point Point_new(lua_State* L, int index)
void Point_finalize(script::ContextHandle handle, void* data)
{ {
auto pt = (gfx::Point*)data; gfx::Point pt(0, 0);
delete pt;
}
void Point_new(script::ContextHandle handle)
{
script::Context ctx(handle);
auto pt = new gfx::Point(0, 0);
// Copy other rectangle // Copy other rectangle
if (ctx.isUserData(1, kTag)) { if (auto pt2 = may_get_obj<gfx::Point>(L, index)) {
auto pt2 = (gfx::Point*)ctx.toUserData(1, kTag); pt = *pt2;
*pt = *pt2;
} }
// Convert { x, y } into a Point // Convert { x, y } into a Point
else if (ctx.isObject(1)) { else if (lua_istable(L, index)) {
ctx.getProp(1, "x"); lua_getfield(L, index, "x");
ctx.getProp(1, "y"); lua_getfield(L, index, "y");
pt->x = ctx.toInt(-2); pt.x = lua_tointeger(L, -2);
pt->y = ctx.toInt(-1); pt.y = lua_tointeger(L, -1);
ctx.pop(2); lua_pop(L, 2);
} }
else if (ctx.isNumber(1)) { else {
pt->x = ctx.toInt(1); pt.x = lua_tointeger(L, index);
pt->y = ctx.toInt(2); pt.y = lua_tointeger(L, index+1);
} }
return pt;
ctx.newObject(kTag, pt, Point_finalize);
} }
void Point_get_x(script::ContextHandle handle) int Point_new(lua_State* L)
{ {
script::Context ctx(handle); push_obj(L, Point_new(L, 1));
auto pt = (gfx::Point*)ctx.toUserData(0, kTag); return 1;
ASSERT(pt);
ctx.pushInt(pt->x);
} }
void Point_get_y(script::ContextHandle handle) int Point_get_x(lua_State* L)
{ {
script::Context ctx(handle); const auto pt = get_obj<gfx::Point>(L, 1);
auto pt = (gfx::Point*)ctx.toUserData(0, kTag); lua_pushinteger(L, pt->x);
ctx.pushInt(pt->y); return 1;
} }
void Point_set_x(script::ContextHandle handle) int Point_get_y(lua_State* L)
{ {
script::Context ctx(handle); const auto pt = get_obj<gfx::Point>(L, 1);
auto pt = (gfx::Point*)ctx.toUserData(0, kTag); lua_pushinteger(L, pt->y);
ASSERT(pt); return 1;
pt->x = ctx.toInt(1);
} }
void Point_set_y(script::ContextHandle handle) int Point_set_x(lua_State* L)
{ {
script::Context ctx(handle); auto pt = get_obj<gfx::Point>(L, 1);
auto pt = (gfx::Point*)ctx.toUserData(0, kTag); pt->x = lua_tointeger(L, 2);
pt->y = ctx.toInt(1); return 0;
} }
const script::FunctionEntry Point_methods[] = { int Point_set_y(lua_State* L)
{ nullptr, nullptr, 0 } {
auto pt = get_obj<gfx::Point>(L, 1);
pt->y = lua_tointeger(L, 2);
return 0;
}
const luaL_Reg Point_methods[] = {
{ nullptr, nullptr }
}; };
const script::PropertyEntry Point_props[] = { const Property Point_properties[] = {
{ "x", Point_get_x, Point_set_x }, { "x", Point_get_x, Point_set_x },
{ "y", Point_get_y, Point_set_y }, { "y", Point_get_y, Point_set_y },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_point_class(script::index_t idx, script::Context& ctx) DEF_MTNAME(gfx::Point);
void register_point_class(lua_State* L)
{ {
ctx.registerClass(idx, kTag, using gfx::Point;
Point_new, 3, REG_CLASS(L, Point);
Point_methods, REG_CLASS_NEW(L, Point);
Point_props); REG_CLASS_PROPERTIES(L, Point);
} }
void push_new_point(script::Context& ctx, const gfx::Point& pt) gfx::Point convert_args_into_point(lua_State* L, int index)
{ {
ctx.newObject(kTag, new gfx::Point(pt), Point_finalize); return Point_new(L, index);
}
gfx::Point convert_args_into_point(script::Context& ctx)
{
gfx::Point result;
Point_new(ctx.handle());
auto pt = (gfx::Point*)ctx.toUserData(-1, kTag);
if (pt)
result = *pt;
ctx.pop(1);
return result;
} }
} // namespace script
} // namespace app } // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2017 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -8,151 +8,132 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/script/luacpp.h"
#include "gfx/rect.h" #include "gfx/rect.h"
#include "script/engine.h"
namespace app { namespace app {
namespace script {
namespace { namespace {
const char* kTag = "Rectangle"; gfx::Rect Rectangle_new(lua_State* L, int index)
void Rectangle_finalize(script::ContextHandle handle, void* data)
{ {
auto rc = (gfx::Rect*)data; gfx::Rect rc(0, 0, 0, 0);
delete rc;
}
void Rectangle_new(script::ContextHandle handle)
{
script::Context ctx(handle);
auto rc = new gfx::Rect(0, 0, 0, 0);
// Copy other rectangle // Copy other rectangle
if (ctx.isUserData(1, kTag)) { if (auto rc2 = may_get_obj<gfx::Rect>(L, index)) {
auto rc2 = (gfx::Rect*)ctx.toUserData(1, kTag); rc = *rc2;
*rc = *rc2;
} }
// Convert { x, y, width, height } into a Rectangle // Convert { x, y, width, height } into a Rectangle
else if (ctx.isObject(1)) { else if (lua_istable(L, index)) {
ctx.getProp(1, "x"); lua_getfield(L, index, "x");
ctx.getProp(1, "y"); lua_getfield(L, index, "y");
ctx.getProp(1, "width"); lua_getfield(L, index, "width");
ctx.getProp(1, "height"); lua_getfield(L, index, "height");
rc->x = ctx.toInt(-4); rc.x = lua_tointeger(L, -4);
rc->y = ctx.toInt(-3); rc.y = lua_tointeger(L, -3);
rc->w = ctx.toInt(-2); rc.w = lua_tointeger(L, -2);
rc->h = ctx.toInt(-1); rc.h = lua_tointeger(L, -1);
ctx.pop(4); lua_pop(L, 4);
} }
else if (ctx.isNumber(1)) { else {
rc->x = ctx.toInt(1); rc.x = lua_tointeger(L, index);
rc->y = ctx.toInt(2); rc.y = lua_tointeger(L, index+1);
rc->w = ctx.toInt(3); rc.w = lua_tointeger(L, index+2);
rc->h = ctx.toInt(4); rc.h = lua_tointeger(L, index+3);
} }
return rc;
ctx.newObject(kTag, rc, Rectangle_finalize);
} }
void Rectangle_get_x(script::ContextHandle handle) int Rectangle_new(lua_State* L)
{ {
script::Context ctx(handle); push_obj(L, Rectangle_new(L, 1));
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); return 1;
ASSERT(rc);
ctx.pushInt(rc->x);
} }
void Rectangle_get_y(script::ContextHandle handle) int Rectangle_get_x(lua_State* L)
{ {
script::Context ctx(handle); const auto rc = get_obj<gfx::Rect>(L, 1);
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); lua_pushinteger(L, rc->x);
ctx.pushInt(rc->y); return 1;
} }
void Rectangle_get_width(script::ContextHandle handle) int Rectangle_get_y(lua_State* L)
{ {
script::Context ctx(handle); const auto rc = get_obj<gfx::Rect>(L, 1);
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); lua_pushinteger(L, rc->y);
ASSERT(rc); return 1;
ctx.pushInt(rc->w);
} }
void Rectangle_get_height(script::ContextHandle handle) int Rectangle_get_width(lua_State* L)
{ {
script::Context ctx(handle); const auto rc = get_obj<gfx::Rect>(L, 1);
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); lua_pushinteger(L, rc->w);
ASSERT(rc); return 1;
ctx.pushInt(rc->h);
} }
void Rectangle_set_x(script::ContextHandle handle) int Rectangle_get_height(lua_State* L)
{ {
script::Context ctx(handle); const auto rc = get_obj<gfx::Rect>(L, 1);
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); lua_pushinteger(L, rc->h);
ASSERT(rc); return 1;
rc->x = ctx.toInt(1);
} }
void Rectangle_set_y(script::ContextHandle handle) int Rectangle_set_x(lua_State* L)
{ {
script::Context ctx(handle); auto rc = get_obj<gfx::Rect>(L, 1);
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); rc->x = lua_tointeger(L, 2);
rc->y = ctx.toInt(1); return 0;
} }
void Rectangle_set_width(script::ContextHandle handle) int Rectangle_set_y(lua_State* L)
{ {
script::Context ctx(handle); auto rc = get_obj<gfx::Rect>(L, 1);
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); rc->y = lua_tointeger(L, 2);
ASSERT(rc); return 0;
rc->w = ctx.toInt(1);
} }
void Rectangle_set_height(script::ContextHandle handle) int Rectangle_set_width(lua_State* L)
{ {
script::Context ctx(handle); auto rc = get_obj<gfx::Rect>(L, 1);
auto rc = (gfx::Rect*)ctx.toUserData(0, kTag); rc->w = lua_tointeger(L, 2);
ASSERT(rc); return 0;
rc->h = ctx.toInt(1);
} }
const script::FunctionEntry Rectangle_methods[] = { int Rectangle_set_height(lua_State* L)
{ nullptr, nullptr, 0 } {
auto rc = get_obj<gfx::Rect>(L, 1);
rc->h = lua_tointeger(L, 2);
return 0;
}
const luaL_Reg Rectangle_methods[] = {
{ nullptr, nullptr }
}; };
const script::PropertyEntry Rectangle_props[] = { const Property Rectangle_properties[] = {
{ "x", Rectangle_get_x, Rectangle_set_x }, { "x", Rectangle_get_x, Rectangle_set_x },
{ "y", Rectangle_get_y, Rectangle_set_y }, { "y", Rectangle_get_y, Rectangle_set_y },
{ "width", Rectangle_get_width, Rectangle_set_width }, { "width", Rectangle_get_width, Rectangle_set_width },
{ "height", Rectangle_get_height, Rectangle_set_height }, { "height", Rectangle_get_height, Rectangle_set_height },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_rectangle_class(script::index_t idx, script::Context& ctx) DEF_MTNAME(gfx::Rect);
void register_rect_class(lua_State* L)
{ {
ctx.registerClass(idx, kTag, using Rectangle = gfx::Rect;
Rectangle_new, 3, REG_CLASS(L, Rectangle);
Rectangle_methods, REG_CLASS_NEW(L, Rectangle);
Rectangle_props); REG_CLASS_PROPERTIES(L, Rectangle);
} }
void push_new_rectangle(script::Context& ctx, const gfx::Rect& rc) gfx::Rect convert_args_into_rect(lua_State* L, int index)
{ {
ctx.newObject(kTag, new gfx::Rect(rc), Rectangle_finalize); return Rectangle_new(L, index);
}
gfx::Rect convert_args_into_rectangle(script::Context& ctx)
{
gfx::Rect result;
Rectangle_new(ctx.handle());
auto rc = (gfx::Rect*)ctx.toUserData(-1, kTag);
if (rc)
result = *rc;
ctx.pop(1);
return result;
} }
} // namespace script
} // namespace app } // namespace app

View File

@ -11,20 +11,19 @@
#include "app/cmd/deselect_mask.h" #include "app/cmd/deselect_mask.h"
#include "app/cmd/set_mask.h" #include "app/cmd/set_mask.h"
#include "app/doc.h" #include "app/doc.h"
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/transaction.h" #include "app/transaction.h"
#include "app/tx.h" #include "app/tx.h"
#include "doc/mask.h" #include "doc/mask.h"
#include "script/engine.h"
namespace app { namespace app {
namespace script {
using namespace doc; using namespace doc;
namespace { namespace {
const char* kTag = "Selection";
struct SelectionObject { struct SelectionObject {
Mask* mask; Mask* mask;
Sprite* sprite; Sprite* sprite;
@ -32,29 +31,35 @@ struct SelectionObject {
: mask(mask) : mask(mask)
, sprite(sprite) { , sprite(sprite) {
} }
SelectionObject(SelectionObject&& that)
: mask(that.mask)
, sprite(that.sprite) {
that.mask = nullptr;
that.sprite = nullptr;
}
~SelectionObject() { ~SelectionObject() {
if (!sprite && mask) if (!sprite && mask)
delete mask; delete mask;
} }
SelectionObject(const SelectionObject&) = delete;
SelectionObject& operator=(const SelectionObject&) = delete;
}; };
void Selection_finalize(script::ContextHandle handle, void* data) int Selection_new(lua_State* L)
{ {
auto obj = (SelectionObject*)data; push_obj(L, SelectionObject(new Mask, nullptr));
delete obj; return 1;
} }
void Selection_new(script::ContextHandle handle) int Selection_gc(lua_State* L)
{ {
script::Context ctx(handle); get_obj<SelectionObject>(L, 1)->~SelectionObject();
ctx.newObject(kTag, new SelectionObject(new Mask, nullptr), Selection_finalize); return 0;
} }
void Selection_deselect(script::ContextHandle handle) int Selection_deselect(lua_State* L)
{ {
script::Context ctx(handle); auto obj = get_obj<SelectionObject>(L, 1);
auto obj = (SelectionObject*)ctx.toUserData(0, kTag);
if (obj) { if (obj) {
if (obj->sprite) { if (obj->sprite) {
Doc* doc = static_cast<Doc*>(obj->sprite->document()); Doc* doc = static_cast<Doc*>(obj->sprite->document());
@ -69,21 +74,16 @@ void Selection_deselect(script::ContextHandle handle)
obj->mask->clear(); obj->mask->clear();
} }
} }
ctx.pushUndefined(); return 0;
} }
void Selection_select(script::ContextHandle handle) int Selection_select(lua_State* L)
{ {
script::Context ctx(handle); gfx::Rect bounds = convert_args_into_rect(L, 2);
auto obj = (SelectionObject*)ctx.toUserData(0, kTag); if (bounds.isEmpty())
gfx::Rect bounds = convert_args_into_rectangle(ctx); return Selection_deselect(L);
if (obj) { auto obj = get_obj<SelectionObject>(L, 1);
if (bounds.isEmpty()) {
Selection_deselect(handle);
return;
}
else {
if (obj->sprite) { if (obj->sprite) {
Doc* doc = static_cast<Doc*>(obj->sprite->document()); Doc* doc = static_cast<Doc*>(obj->sprite->document());
ASSERT(doc); ASSERT(doc);
@ -98,17 +98,12 @@ void Selection_select(script::ContextHandle handle)
else { else {
obj->mask->replace(bounds); obj->mask->replace(bounds);
} }
} return 0;
}
ctx.pushUndefined();
} }
void Selection_selectAll(script::ContextHandle handle) int Selection_selectAll(lua_State* L)
{ {
script::Context ctx(handle); auto obj = get_obj<SelectionObject>(L, 1);
auto obj = (SelectionObject*)ctx.toUserData(0, kTag);
if (obj) {
if (obj->sprite) { if (obj->sprite) {
Doc* doc = static_cast<Doc*>(obj->sprite->document()); Doc* doc = static_cast<Doc*>(obj->sprite->document());
@ -124,59 +119,56 @@ void Selection_selectAll(script::ContextHandle handle)
if (!bounds.isEmpty()) if (!bounds.isEmpty())
obj->mask->replace(bounds); obj->mask->replace(bounds);
} }
} return 0;
ctx.pushUndefined();
} }
void Selection_get_bounds(script::ContextHandle handle) int Selection_get_bounds(lua_State* L)
{ {
script::Context ctx(handle); auto obj = get_obj<SelectionObject>(L, 1);
auto obj = (SelectionObject*)ctx.toUserData(0, kTag);
if (obj) {
if (obj->sprite) { if (obj->sprite) {
Doc* doc = static_cast<Doc*>(obj->sprite->document()); Doc* doc = static_cast<Doc*>(obj->sprite->document());
if (doc->isMaskVisible()) { if (doc->isMaskVisible()) {
push_new_rectangle(ctx, doc->mask()->bounds()); push_obj(L, doc->mask()->bounds());
} }
else { // Empty rectangle else { // Empty rectangle
push_new_rectangle(ctx, gfx::Rect(0, 0, 0, 0)); push_obj(L, gfx::Rect(0, 0, 0, 0));
} }
} }
else { else {
push_new_rectangle(ctx, obj->mask->bounds()); push_obj(L, obj->mask->bounds());
} }
} return 1;
else
ctx.pushUndefined();
} }
const script::FunctionEntry Selection_methods[] = { const luaL_Reg Selection_methods[] = {
{ "deselect", Selection_deselect, 1 }, { "deselect", Selection_deselect },
{ "select", Selection_select, 4 }, { "select", Selection_select },
{ "selectAll", Selection_selectAll, 0 }, { "selectAll", Selection_selectAll },
{ nullptr, nullptr, 0 } { "__gc", Selection_gc },
{ nullptr, nullptr }
}; };
const script::PropertyEntry Selection_props[] = { const Property Selection_properties[] = {
{ "bounds", Selection_get_bounds, nullptr }, { "bounds", Selection_get_bounds, nullptr },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_selection_class(script::index_t idx, script::Context& ctx) DEF_MTNAME(SelectionObject);
void register_selection_class(lua_State* L)
{ {
ctx.registerClass(idx, kTag, using Selection = SelectionObject;
Selection_new, 3, REG_CLASS(L, Selection);
Selection_methods, REG_CLASS_NEW(L, Selection);
Selection_props); REG_CLASS_PROPERTIES(L, Selection);
} }
void push_sprite_selection(script::Context& ctx, Sprite* sprite) void push_sprite_selection(lua_State* L, Sprite* sprite)
{ {
ctx.newObject(kTag, push_obj(L, SelectionObject(nullptr, sprite));
new SelectionObject(nullptr, sprite),
Selection_finalize);
} }
} // namespace script
} // namespace app } // namespace app

View File

@ -8,64 +8,54 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/site.h" #include "app/site.h"
namespace app { namespace app {
namespace script {
namespace { namespace {
const char* kTag = "Site"; int Site_get_sprite(lua_State* L)
void Site_finalize(script::ContextHandle handle, void* data)
{ {
auto site = (app::Site*)data; auto site = get_obj<Site>(L, 1);
delete site;
}
void Site_get_sprite(script::ContextHandle handle)
{
script::Context ctx(handle);
auto site = (app::Site*)ctx.toUserData(0, kTag);
if (site->sprite()) if (site->sprite())
push_sprite(ctx, site->sprite()); push_ptr(L, site->sprite());
else else
ctx.pushNull(); lua_pushnil(L);
return 1;
} }
void Site_get_image(script::ContextHandle handle) int Site_get_image(lua_State* L)
{ {
script::Context ctx(handle); auto site = get_obj<Site>(L, 1);
auto site = (app::Site*)ctx.toUserData(0, kTag);
if (site->image()) if (site->image())
push_image(ctx, site->image()); push_ptr(L, site->image());
else else
ctx.pushNull(); lua_pushnil(L);
return 1;
} }
const script::FunctionEntry Site_methods[] = { const luaL_Reg Site_methods[] = {
{ nullptr, nullptr, 0 } { nullptr, nullptr }
}; };
const script::PropertyEntry Site_props[] = { const Property Site_properties[] = {
{ "sprite", Site_get_sprite, nullptr }, { "sprite", Site_get_sprite, nullptr },
{ "image", Site_get_image, nullptr }, { "image", Site_get_image, nullptr },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_site_class(script::index_t idx, script::Context& ctx) DEF_MTNAME(app::Site);
void register_site_class(lua_State* L)
{ {
ctx.registerClass(idx, kTag, REG_CLASS(L, Site);
nullptr, 0, REG_CLASS_PROPERTIES(L, Site);
Site_methods,
Site_props);
}
void push_site(script::Context& ctx, app::Site& site)
{
ctx.newObject(kTag, new app::Site(site), Site_finalize);
} }
} // namespace script
} // namespace app } // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2017 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -8,113 +8,100 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/script/luacpp.h"
#include "gfx/size.h" #include "gfx/size.h"
#include "script/engine.h"
namespace app { namespace app {
namespace script {
namespace { namespace {
const char* kTag = "Size"; gfx::Size Size_new(lua_State* L, int index)
void Size_finalize(script::ContextHandle handle, void* data)
{ {
auto sz = (gfx::Size*)data; gfx::Size sz(0, 0);
delete sz;
}
void Size_new(script::ContextHandle handle)
{
script::Context ctx(handle);
auto sz = new gfx::Size(0, 0);
// Copy other size // Copy other size
if (ctx.isUserData(1, kTag)) { if (auto sz2 = may_get_obj<gfx::Size>(L, index)) {
auto sz2 = (gfx::Size*)ctx.toUserData(1, kTag); sz = *sz2;
*sz = *sz2;
} }
// Convert { width, height } into a Size // Convert { width, height } into a Size
else if (ctx.isObject(1)) { else if (lua_istable(L, index)) {
ctx.getProp(1, "width"); lua_getfield(L, index, "width");
ctx.getProp(1, "height"); lua_getfield(L, index, "height");
sz->w = ctx.toInt(-2); sz.w = lua_tointeger(L, -2);
sz->h = ctx.toInt(-1); sz.h = lua_tointeger(L, -1);
ctx.pop(4); lua_pop(L, 2);
} }
else if (ctx.isNumber(1)) { else {
sz->w = ctx.toInt(1); sz.w = lua_tointeger(L, index);
sz->h = ctx.toInt(2); sz.h = lua_tointeger(L, index+1);
} }
return sz;
ctx.newObject(kTag, sz, Size_finalize);
} }
void Size_get_width(script::ContextHandle handle) int Size_new(lua_State* L)
{ {
script::Context ctx(handle); push_obj(L, Size_new(L, 1));
auto sz = (gfx::Size*)ctx.toUserData(0, kTag); return 1;
ASSERT(sz);
ctx.pushInt(sz->w);
} }
void Size_get_height(script::ContextHandle handle) int Size_get_width(lua_State* L)
{ {
script::Context ctx(handle); const auto sz = get_obj<gfx::Size>(L, 1);
auto sz = (gfx::Size*)ctx.toUserData(0, kTag);
ASSERT(sz); ASSERT(sz);
ctx.pushInt(sz->h); lua_pushinteger(L, sz->w);
return 1;
} }
void Size_set_width(script::ContextHandle handle) int Size_get_height(lua_State* L)
{ {
script::Context ctx(handle); const auto sz = get_obj<gfx::Size>(L, 1);
auto sz = (gfx::Size*)ctx.toUserData(0, kTag);
ASSERT(sz); ASSERT(sz);
sz->w = ctx.toInt(1); lua_pushinteger(L, sz->h);
return 1;
} }
void Size_set_height(script::ContextHandle handle) int Size_set_width(lua_State* L)
{ {
script::Context ctx(handle); auto sz = get_obj<gfx::Size>(L, 1);
auto sz = (gfx::Size*)ctx.toUserData(0, kTag);
ASSERT(sz); ASSERT(sz);
sz->h = ctx.toInt(1); sz->w = lua_tointeger(L, 2);
return 0;
} }
const script::FunctionEntry Size_methods[] = { int Size_set_height(lua_State* L)
{ nullptr, nullptr, 0 } {
auto sz = get_obj<gfx::Size>(L, 1);
ASSERT(sz);
sz->h = lua_tointeger(L, 2);
return 0;
}
const luaL_Reg Size_methods[] = {
{ nullptr, nullptr }
}; };
const script::PropertyEntry Size_props[] = { const Property Size_properties[] = {
{ "width", Size_get_width, Size_set_width }, { "width", Size_get_width, Size_set_width },
{ "height", Size_get_height, Size_set_height }, { "height", Size_get_height, Size_set_height },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_size_class(script::index_t idx, script::Context& ctx) DEF_MTNAME(gfx::Size);
void register_size_class(lua_State* L)
{ {
ctx.registerClass(idx, kTag, using gfx::Size;
Size_new, 3, REG_CLASS(L, Size);
Size_methods, REG_CLASS_NEW(L, Size);
Size_props); REG_CLASS_PROPERTIES(L, Size);
} }
void push_new_size(script::Context& ctx, const gfx::Size& sz) gfx::Size convert_args_into_size(lua_State* L, int index)
{ {
ctx.newObject(kTag, new gfx::Size(sz), Size_finalize); return Size_new(L, index);
}
gfx::Size convert_args_into_size(script::Context& ctx)
{
gfx::Size result;
Size_new(ctx.handle());
auto sz = (gfx::Size*)ctx.toUserData(-1, kTag);
if (sz)
result = *sz;
ctx.pop(1);
return result;
} }
} // namespace script
} // namespace app } // namespace app

View File

@ -16,7 +16,8 @@
#include "app/doc.h" #include "app/doc.h"
#include "app/doc_api.h" #include "app/doc_api.h"
#include "app/file/palette_file.h" #include "app/file/palette_file.h"
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/site.h" #include "app/site.h"
#include "app/transaction.h" #include "app/transaction.h"
#include "app/tx.h" #include "app/tx.h"
@ -24,65 +25,57 @@
#include "doc/mask.h" #include "doc/mask.h"
#include "doc/palette.h" #include "doc/palette.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "script/engine.h"
namespace app { namespace app {
namespace script {
namespace { namespace {
const char* kTag = "Sprite"; int Sprite_new(lua_State* L)
void Sprite_new(script::ContextHandle handle)
{ {
script::Context ctx(handle); const int w = lua_tointeger(L, 1);
int w = ctx.requireInt(1); const int h = lua_tointeger(L, 2);
int h = ctx.requireInt(2); const int colorMode = (lua_isnone(L, 3) ? IMAGE_RGB: lua_tointeger(L, 3));
int colorMode = (ctx.isUndefined(3) ? IMAGE_RGB: ctx.requireInt(3));
std::unique_ptr<Sprite> sprite( std::unique_ptr<Sprite> sprite(
Sprite::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256)); Sprite::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256));
std::unique_ptr<Doc> doc(new Doc(sprite.get())); std::unique_ptr<Doc> doc(new Doc(sprite.get()));
sprite.release(); sprite.release();
app::Context* appCtx = App::instance()->context(); app::Context* ctx = App::instance()->context();
doc->setContext(appCtx); doc->setContext(ctx);
ctx.newObject(kTag, doc->sprite(), nullptr); push_ptr(L, doc->sprite());
doc.release(); doc.release();
return 1;
} }
void Sprite_resize(script::ContextHandle handle) int Sprite_resize(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
Sprite* sprite = (Sprite*)ctx.toUserData(0, kTag); const gfx::Size size = convert_args_into_size(L, 2);
gfx::Size size = convert_args_into_size(ctx);
if (sprite) {
Doc* doc = static_cast<Doc*>(sprite->document()); Doc* doc = static_cast<Doc*>(sprite->document());
Tx tx; Tx tx;
DocApi(doc, tx).setSpriteSize(doc->sprite(), size.w, size.h); DocApi(doc, tx).setSpriteSize(doc->sprite(), size.w, size.h);
tx.commit(); tx.commit();
} return 0;
ctx.pushUndefined();
} }
void Sprite_crop(script::ContextHandle handle) int Sprite_crop(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (doc::Sprite*)ctx.toUserData(0, kTag);
if (sprite) {
Doc* doc = static_cast<Doc*>(sprite->document()); Doc* doc = static_cast<Doc*>(sprite->document());
gfx::Rect bounds; gfx::Rect bounds;
// Use mask bounds // Use mask bounds
if (ctx.isUndefined(1)) { if (lua_isnone(L, 2)) {
if (doc->isMaskVisible()) if (doc->isMaskVisible())
bounds = doc->mask()->bounds(); bounds = doc->mask()->bounds();
else else
bounds = sprite->bounds(); bounds = sprite->bounds();
} }
else { else {
bounds = convert_args_into_rectangle(ctx); bounds = convert_args_into_rect(L, 2);
} }
if (!bounds.isEmpty()) { if (!bounds.isEmpty()) {
@ -90,14 +83,12 @@ void Sprite_crop(script::ContextHandle handle)
DocApi(doc, tx).cropSprite(sprite, bounds); DocApi(doc, tx).cropSprite(sprite, bounds);
tx.commit(); tx.commit();
} }
} return 0;
ctx.pushUndefined();
} }
void Sprite_save(script::ContextHandle handle) int Sprite_save(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (Sprite*)ctx.toUserData(0, kTag);
if (sprite) { if (sprite) {
Doc* doc = static_cast<Doc*>(sprite->document()); Doc* doc = static_cast<Doc*>(sprite->document());
app::Context* appCtx = App::instance()->context(); app::Context* appCtx = App::instance()->context();
@ -106,16 +97,13 @@ void Sprite_save(script::ContextHandle handle)
Commands::instance()->byId(CommandId::SaveFile()); Commands::instance()->byId(CommandId::SaveFile());
appCtx->executeCommand(saveCommand); appCtx->executeCommand(saveCommand);
} }
return 0;
ctx.pushUndefined();
} }
void Sprite_saveAs(script::ContextHandle handle) int Sprite_saveAs(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (Sprite*)ctx.toUserData(0, kTag); const char* fn = luaL_checkstring(L, 2);
const char* fn = ctx.requireString(1);
if (fn && sprite) { if (fn && sprite) {
Doc* doc = static_cast<Doc*>(sprite->document()); Doc* doc = static_cast<Doc*>(sprite->document());
app::Context* appCtx = App::instance()->context(); app::Context* appCtx = App::instance()->context();
@ -128,16 +116,13 @@ void Sprite_saveAs(script::ContextHandle handle)
doc->setFilename(fn); doc->setFilename(fn);
appCtx->executeCommand(saveCommand, params); appCtx->executeCommand(saveCommand, params);
} }
return 0;
ctx.pushUndefined();
} }
void Sprite_saveCopyAs(script::ContextHandle handle) int Sprite_saveCopyAs(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (Sprite*)ctx.toUserData(0, kTag); const char* fn = luaL_checkstring(L, 2);
const char* fn = ctx.requireString(1);
if (fn && sprite) { if (fn && sprite) {
Doc* doc = static_cast<Doc*>(sprite->document()); Doc* doc = static_cast<Doc*>(sprite->document());
app::Context* appCtx = App::instance()->context(); app::Context* appCtx = App::instance()->context();
@ -150,16 +135,13 @@ void Sprite_saveCopyAs(script::ContextHandle handle)
params.set("filename", fn); params.set("filename", fn);
appCtx->executeCommand(saveCommand, params); appCtx->executeCommand(saveCommand, params);
} }
return 0;
ctx.pushUndefined();
} }
void Sprite_loadPalette(script::ContextHandle handle) int Sprite_loadPalette(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (doc::Sprite*)ctx.toUserData(0, kTag); const char* fn = luaL_checkstring(L, 2);
const char* fn = ctx.toString(1);
if (fn && sprite) { if (fn && sprite) {
Doc* doc = static_cast<Doc*>(sprite->document()); Doc* doc = static_cast<Doc*>(sprite->document());
std::unique_ptr<doc::Palette> palette(load_palette(fn)); std::unique_ptr<doc::Palette> palette(load_palette(fn));
@ -170,100 +152,94 @@ void Sprite_loadPalette(script::ContextHandle handle)
tx.commit(); tx.commit();
} }
} }
return 0;
ctx.pushUndefined();
} }
void Sprite_get_filename(script::ContextHandle handle) int Sprite_get_filename(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (doc::Sprite*)ctx.toUserData(0, kTag); lua_pushstring(L, sprite->document()->filename().c_str());
ctx.pushString(sprite->document()->filename().c_str()); return 1;
} }
void Sprite_get_width(script::ContextHandle handle) int Sprite_get_width(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (doc::Sprite*)ctx.toUserData(0, kTag); lua_pushinteger(L, sprite->width());
ctx.pushInt(sprite->width()); return 1;
} }
void Sprite_set_width(script::ContextHandle handle) int Sprite_get_height(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (Sprite*)ctx.toUserData(0, kTag); lua_pushinteger(L, sprite->height());
const int width = ctx.requireInt(1); return 1;
}
int Sprite_get_colorMode(lua_State* L)
{
auto sprite = get_ptr<Sprite>(L, 1);
lua_pushinteger(L, sprite->pixelFormat());
return 1;
}
int Sprite_get_selection(lua_State* L)
{
auto sprite = get_ptr<Sprite>(L, 1);
push_sprite_selection(L, sprite);
return 1;
}
int Sprite_set_width(lua_State* L)
{
auto sprite = get_ptr<Sprite>(L, 1);
const int width = lua_tointeger(L, 2);
Tx tx; Tx tx;
tx(new cmd::SetSpriteSize(sprite, tx(new cmd::SetSpriteSize(sprite, width, sprite->height()));
width, sprite->height()));
tx.commit(); tx.commit();
ctx.pushUndefined(); return 0;
} }
void Sprite_get_height(script::ContextHandle handle) int Sprite_set_height(lua_State* L)
{ {
script::Context ctx(handle); auto sprite = get_ptr<Sprite>(L, 1);
auto sprite = (doc::Sprite*)ctx.toUserData(0, kTag); const int height = lua_tointeger(L, 2);
ctx.pushInt(sprite->height());
}
void Sprite_set_height(script::ContextHandle handle)
{
script::Context ctx(handle);
auto sprite = (Sprite*)ctx.toUserData(0, kTag);
const int height = ctx.requireInt(1);
Tx tx; Tx tx;
tx(new cmd::SetSpriteSize(sprite, tx(new cmd::SetSpriteSize(sprite, sprite->width(), height));
sprite->width(), height));
tx.commit(); tx.commit();
ctx.pushUndefined(); return 0;
} }
void Sprite_get_colorMode(script::ContextHandle handle) const luaL_Reg Sprite_methods[] = {
{ { "resize", Sprite_resize },
script::Context ctx(handle); { "crop", Sprite_crop },
auto sprite = (doc::Sprite*)ctx.toUserData(0, kTag); { "save", Sprite_save },
ctx.pushInt(sprite->pixelFormat()); { "saveAs", Sprite_saveAs },
} { "saveCopyAs", Sprite_saveCopyAs },
{ "loadPalette", Sprite_loadPalette },
void Sprite_get_selection(script::ContextHandle handle) { nullptr, nullptr }
{
script::Context ctx(handle);
auto sprite = (doc::Sprite*)ctx.toUserData(0, kTag);
push_sprite_selection(ctx, sprite);
}
const script::FunctionEntry Sprite_methods[] = {
{ "resize", Sprite_resize, 2 },
{ "crop", Sprite_crop, 4 },
{ "save", Sprite_save, 2 },
{ "saveAs", Sprite_saveAs, 2 },
{ "saveCopyAs", Sprite_saveCopyAs, 2 },
{ "loadPalette", Sprite_loadPalette, 1 },
{ nullptr, nullptr, 0 }
}; };
const script::PropertyEntry Sprite_props[] = { const Property Sprite_properties[] = {
{ "filename", Sprite_get_filename, nullptr }, { "filename", Sprite_get_filename, nullptr },
{ "width", Sprite_get_width, Sprite_set_width }, { "width", Sprite_get_width, Sprite_set_width },
{ "height", Sprite_get_height, Sprite_set_height }, { "height", Sprite_get_height, Sprite_set_height },
{ "colorMode", Sprite_get_colorMode, nullptr }, { "colorMode", Sprite_get_colorMode, nullptr },
{ "selection", Sprite_get_selection, nullptr }, { "selection", Sprite_get_selection, nullptr },
{ nullptr, nullptr, 0 } { nullptr, nullptr, nullptr }
}; };
} // anonymous namespace } // anonymous namespace
void register_sprite_class(script::index_t idx, script::Context& ctx) DEF_MTNAME(doc::Sprite);
void register_sprite_class(lua_State* L)
{ {
ctx.registerClass(idx, kTag, using doc::Sprite;
Sprite_new, 3, REG_CLASS(L, Sprite);
Sprite_methods, Sprite_props); REG_CLASS_NEW(L, Sprite);
} REG_CLASS_PROPERTIES(L, Sprite);
void push_sprite(script::Context& ctx, Sprite* sprite)
{
ctx.newObject(kTag, sprite, nullptr);
} }
} // namespace script
} // namespace app } // namespace app

View File

@ -31,10 +31,10 @@ Shell::~Shell()
void Shell::run(script::Engine& engine) void Shell::run(script::Engine& engine)
{ {
std::cout << "Welcome to " PACKAGE " v" VERSION " interactive console" << std::endl; std::cout << "Welcome to " PACKAGE " v" VERSION " Interactive Console" << std::endl;
std::string line; std::string line;
while (std::getline(std::cin, line)) { while (std::getline(std::cin, line)) {
engine.eval(line); engine.evalCode(line);
} }
std::cout << "Done\n"; std::cout << "Done\n";
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -12,11 +12,10 @@
#error ENABLE_SCRIPTING must be defined #error ENABLE_SCRIPTING must be defined
#endif #endif
namespace script {
class Engine;
}
namespace app { namespace app {
namespace script {
class Engine;
}
class Shell { class Shell {
public: public:

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -63,7 +63,7 @@ protected:
DevConsoleView::DevConsoleView() DevConsoleView::DevConsoleView()
: Box(VERTICAL) : Box(VERTICAL)
, m_textBox("Welcome to Aseprite JavaScript Console\n(Experimental)", LEFT) , m_textBox("Welcome to " PACKAGE " v" VERSION " Console\n(Experimental)", LEFT)
, m_label(">") , m_label(">")
, m_entry(new CommmandEntry) , m_entry(new CommmandEntry)
, m_engine(this) , m_engine(this)
@ -137,7 +137,7 @@ bool DevConsoleView::onProcessMessage(Message* msg)
void DevConsoleView::onExecuteCommand(const std::string& cmd) void DevConsoleView::onExecuteCommand(const std::string& cmd)
{ {
m_engine.printLastResult(); m_engine.printLastResult();
m_engine.eval(cmd); m_engine.evalCode(cmd);
} }
void DevConsoleView::onConsolePrint(const char* text) void DevConsoleView::onConsolePrint(const char* text)

View File

@ -12,10 +12,9 @@
#error ENABLE_SCRIPTING must be defined #error ENABLE_SCRIPTING must be defined
#endif #endif
#include "app/script/app_scripting.h" #include "app/script/engine.h"
#include "app/ui/tabs.h" #include "app/ui/tabs.h"
#include "app/ui/workspace_view.h" #include "app/ui/workspace_view.h"
#include "script/engine_delegate.h"
#include "ui/box.h" #include "ui/box.h"
#include "ui/label.h" #include "ui/label.h"
#include "ui/textbox.h" #include "ui/textbox.h"
@ -57,7 +56,7 @@ namespace app {
ui::HBox m_bottomBox; ui::HBox m_bottomBox;
ui::Label m_label; ui::Label m_label;
CommmandEntry* m_entry; CommmandEntry* m_entry;
AppScripting m_engine; script::Engine m_engine;
}; };
} // namespace app } // namespace app

View File

@ -1,8 +0,0 @@
# Aseprite Scripting Library
# Copyright (C) 2015-2017 David Capello
add_library(script-lib engine.cpp engine_delegate.cpp)
target_link_libraries(script-lib
laf-base
mujs)

View File

@ -1,20 +0,0 @@
Copyright (c) 2015-2016 David Capello
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.

View File

@ -1,4 +0,0 @@
# Aseprite Scripting Library
*Copyright (C) 2015-2017 David Capello*
> Distributed under [MIT license](LICENSE.txt)

View File

@ -1,460 +0,0 @@
// Aseprite Scripting Library
// Copyright (c) 2015-2017 David Capello
//
// 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 "script/engine.h"
#include "base/convert_to.h"
#include "base/exception.h"
#include "base/file_handle.h"
#include "base/fstream_path.h"
#include "base/memory.h"
#include "script/engine_delegate.h"
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
extern "C" {
#include <mujs/mujs.h>
}
namespace script {
static const char* stacktrace_js =
"Error.prototype.toString = function() {\n"
" if (this.stackTrace)\n"
" return this.name + ': ' + this.message + this.stackTrace;\n"
" return this.name + ': ' + this.message;\n"
"}";
void Context::setContextUserData(void* userData)
{
js_setcontext(m_handle, userData);
}
void* Context::getContextUserData()
{
return js_getcontext(m_handle);
}
void Context::error(const char* err)
{
js_error(m_handle, "%s", err);
}
void Context::pop()
{
js_pop(m_handle, 1);
}
void Context::pop(index_t count)
{
js_pop(m_handle, count);
}
void Context::remove(index_t idx)
{
js_remove(m_handle, idx);
}
void Context::duplicateTop()
{
js_dup(m_handle);
}
index_t Context::top()
{
return js_gettop(m_handle);
}
void Context::copy(index_t i)
{
js_copy(m_handle, i);
}
void Context::call(index_t args)
{
js_call(m_handle, args);
}
bool Context::isUndefined(index_t i)
{
return (js_isundefined(m_handle, i) ? true: false);
}
bool Context::isNull(index_t i)
{
return (js_isnull(m_handle, i) ? true: false);
}
bool Context::isNullOrUndefined(index_t i)
{
return (js_isnull(m_handle, i) ||
js_isundefined(m_handle, i)? true: false);
}
bool Context::isBool(index_t i)
{
return (js_isboolean(m_handle, i) ? true: false);
}
bool Context::isNumber(index_t i)
{
return (js_isnumber(m_handle, i) ? true: false);
}
bool Context::isString(index_t i)
{
return (js_isstring(m_handle, i) ? true: false);
}
bool Context::isObject(index_t i)
{
return (js_isobject(m_handle, i) ? true: false);
}
bool Context::isArray(index_t i)
{
return (js_isarray(m_handle, i) ? true: false);
}
bool Context::isCallable(index_t i)
{
return (js_iscallable(m_handle, i) ? true: false);
}
bool Context::isUserData(index_t i, const char* tag)
{
return (js_isuserdata(m_handle, i, tag) ? true: false);
}
bool Context::toBool(index_t i)
{
return (js_toboolean(m_handle, i) ? true: false);
}
double Context::toNumber(index_t i)
{
return js_tonumber(m_handle, i);
}
int Context::toInt(index_t i)
{
return js_toint32(m_handle, i);
}
unsigned int Context::toUInt(index_t i)
{
return js_touint32(m_handle, i);
}
const char* Context::toString(index_t i)
{
return js_tostring(m_handle, i);
}
void* Context::toUserData(index_t i, const char* tag)
{
return js_touserdata(m_handle, i, tag);
}
bool Context::requireBool(index_t i)
{
if (js_isboolean(m_handle, i))
return true;
else {
js_typeerror(m_handle, "not a boolean (index %d)\n", i);
return false;
}
}
double Context::requireNumber(index_t i)
{
if (js_isnumber(m_handle, i))
return js_tonumber(m_handle, i);
else {
js_typeerror(m_handle, "not a number (index %d)\n", i);
return 0.0;
}
}
int Context::requireInt(index_t i)
{
return (int)requireNumber(i);
}
unsigned int Context::requireUInt(index_t i)
{
return (unsigned int)requireNumber(i);
}
const char* Context::requireString(index_t i)
{
if (js_isstring(m_handle, i))
return js_tostring(m_handle, i);
else {
js_typeerror(m_handle, "not a string (index %d)\n", i);
return nullptr;
}
}
void* Context::requireUserData(index_t i, const char* tag)
{
if (js_isuserdata(m_handle, i, tag))
return js_touserdata(m_handle, i, tag);
else {
js_typeerror(m_handle, "not a user data (index %d, tag %s)\n", i, tag);
return nullptr;
}
}
void Context::pushUndefined()
{
js_pushundefined(m_handle);
}
void Context::pushNull()
{
js_pushnull(m_handle);
}
void Context::pushBool(bool val)
{
js_pushboolean(m_handle, val);
}
void Context::pushNumber(double val)
{
js_pushnumber(m_handle, val);
}
void Context::pushInt(int val)
{
js_pushnumber(m_handle, double(val));
}
void Context::pushUInt(unsigned int val)
{
js_pushnumber(m_handle, double(val));
}
void Context::pushString(const char* str)
{
js_pushstring(m_handle, str);
}
void Context::pushGlobalObject()
{
js_pushglobal(m_handle);
}
void Context::newObject()
{
js_newobject(m_handle);
}
void Context::newObject(const char* className,
void* userData,
FinalizeFunction finalize)
{
js_getglobal(m_handle, className); // class
js_getproperty(m_handle, -1, "prototype"); // class prototype
js_newuserdata(m_handle, className, userData, finalize); // class userdata
js_rot2(m_handle); // userdata class
js_pop(m_handle, 1); // userdata
}
void Context::newUserData(const char* tag,
void* userData,
FinalizeFunction finalize)
{
js_newuserdata(m_handle, tag, userData, finalize);
}
void Context::registerConstants(index_t idx,
const ConstantEntry* consts)
{
if (idx < 0)
--idx;
for (; consts->id; ++consts) {
js_pushnumber(m_handle, consts->value);
js_defproperty(m_handle, idx, consts->id, JS_DONTENUM);
}
}
void Context::registerProp(index_t idx,
const char* id,
Function getter,
Function setter)
{
if (idx < 0)
idx -= 2;
if (getter)
js_newcfunction(m_handle, getter,
(std::string(id) + ".getter").c_str(), 0);
else
js_pushnull(m_handle);
if (setter)
js_newcfunction(m_handle, setter,
(std::string(id) + ".setter").c_str(), 1);
else
js_pushnull(m_handle);
js_defaccessor(m_handle, idx, id, JS_DONTENUM);
}
void Context::registerProps(index_t idx, const PropertyEntry* props)
{
for (int i=0; props[i].id; ++i) {
registerProp(idx,
props[i].id,
props[i].getter,
props[i].setter);
}
}
void Context::registerFunc(index_t idx,
const char* id,
const Function func,
index_t nargs)
{
js_newcfunction(m_handle, func, id, nargs);
js_defproperty(m_handle, idx, id, JS_DONTENUM);
}
void Context::registerFuncs(index_t idx, const FunctionEntry* methods)
{
if (idx < 0)
--idx;
for (; methods->id; ++methods) {
registerFunc(idx,
methods->id,
methods->value,
methods->nargs);
}
}
void Context::registerObject(index_t idx,
const char* id,
const FunctionEntry* methods,
const PropertyEntry* props)
{
if (idx < 0)
--idx;
newObject();
if (methods) registerFuncs(-1, methods);
if (props) registerProps(-1, props);
js_defproperty(m_handle, idx, id, JS_DONTENUM);
}
void Context::registerClass(index_t idx,
const char* id,
Function ctorFunc, int ctorNargs,
const FunctionEntry* methods,
const PropertyEntry* props)
{
if (idx < 0)
idx -= 2;
js_getglobal(m_handle, "Object");
js_getproperty(m_handle, -1, "prototype");
js_newuserdata(m_handle, id, nullptr, nullptr);
if (methods) registerFuncs(-1, methods);
if (props) registerProps(-1, props);
js_newcconstructor(m_handle, ctorFunc, ctorFunc, id, ctorNargs);
js_defproperty(m_handle, idx, id, JS_DONTENUM);
js_pop(m_handle, 1); // pop Object
}
bool Context::hasProp(index_t i, const char* propName)
{
return (js_hasproperty(m_handle, i, propName) ? true: false);
}
void Context::getProp(index_t i, const char* propName)
{
js_getproperty(m_handle, i, propName);
}
void Context::setProp(index_t i, const char* propName)
{
js_defproperty(m_handle, i, propName, JS_DONTENUM);
}
Engine::Engine(EngineDelegate* delegate)
: m_ctx(js_newstate(NULL, NULL, JS_STRICT))
, m_delegate(delegate)
, m_printLastResult(false)
{
// Pre-scripts
js_dostring(m_ctx.handle(), stacktrace_js);
}
Engine::~Engine()
{
js_freestate(m_ctx.handle());
}
void Engine::printLastResult()
{
m_printLastResult = true;
}
bool Engine::eval(const std::string& jsCode,
const std::string& filename)
{
bool errFlag = true;
onBeforeEval();
ContextHandle handle = m_ctx.handle();
if (js_ploadstring(handle, filename.c_str(), jsCode.c_str()) == 0) {
js_pushundefined(handle);
if (js_pcall(handle, 0) == 0) {
if (m_printLastResult) {
if (!js_isundefined(handle, -1)) {
const char* result = js_tostring(handle, -1);
if (result)
m_delegate->onConsolePrint(result);
}
}
errFlag = false;
}
}
// Print error message
if (errFlag) {
std::string err;
const char* s = js_trystring(handle, -1, "Error");
if (s)
m_delegate->onConsolePrint(s);
}
js_pop(handle, 1);
onAfterEval(errFlag);
return !errFlag;
}
bool Engine::evalFile(const std::string& filename)
{
std::stringstream buf;
{
std::ifstream s(FSTREAM_PATH(filename));
buf << s.rdbuf();
}
return eval(buf.str(), filename);
}
} // namespace script

View File

@ -1,165 +0,0 @@
// Aseprite Scripting Library
// Copyright (c) 2015-2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef SCRIPT_ENGINE_H_INCLUDED
#define SCRIPT_ENGINE_H_INCLUDED
#pragma once
#include <string>
struct js_State;
namespace script {
class Context;
class EngineDelegate;
typedef int result_t;
typedef int index_t;
typedef js_State* ContextHandle;
typedef void (*Function)(ContextHandle);
typedef void (*FinalizeFunction)(ContextHandle, void*);
struct FunctionEntry {
const char* id;
Function value;
index_t nargs;
};
struct PropertyEntry {
const char* id;
Function getter;
Function setter;
};
struct ConstantEntry {
const char* id;
double value;
};
class Module {
public:
virtual ~Module() { }
virtual const char* id() const = 0;
virtual int registerModule(Context& ctx) = 0;
};
class Context {
public:
Context(ContextHandle handle) : m_handle(handle) { }
ContextHandle handle() { return m_handle; }
void setContextUserData(void* userData);
void* getContextUserData();
void error(const char* err);
void pop();
void pop(index_t count);
void remove(index_t idx);
void duplicateTop();
index_t top();
void copy(index_t i);
void call(index_t args);
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 isString(index_t i);
bool isObject(index_t i);
bool isArray(index_t i);
bool isUserData(index_t i, const char* tag);
bool isCallable(index_t i);
bool toBool(index_t i);
double toNumber(index_t i);
int toInt(index_t i);
unsigned int toUInt(index_t i);
const char* toString(index_t i);
void* toUserData(index_t i, const char* tag);
bool hasProp(index_t i, const char* propName);
void getProp(index_t i, const char* propName);
void setProp(index_t i, const char* propName);
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* requireUserData(index_t i, const char* tag);
void pushUndefined();
void pushNull();
void pushBool(bool val);
void pushNumber(double val);
void pushInt(int val);
void pushUInt(unsigned int val);
void pushString(const char* str);
void pushGlobalObject();
void newObject();
void newObject(const char* className,
void* userData,
FinalizeFunction finalize);
void newUserData(const char* tag,
void* userData,
FinalizeFunction finalize);
void registerConstants(index_t idx,
const ConstantEntry* consts);
void registerProp(index_t idx,
const char* id,
Function getter,
Function setter);
void registerProps(index_t idx,
const PropertyEntry* props);
void registerFunc(index_t idx,
const char* id,
const Function func,
index_t nargs);
void registerFuncs(index_t idx,
const FunctionEntry* methods);
void registerObject(index_t idx,
const char* id,
const FunctionEntry* methods,
const PropertyEntry* props);
void registerClass(index_t idx,
const char* id,
Function ctorFunc, int ctorNargs,
const FunctionEntry* methods,
const PropertyEntry* props);
private:
ContextHandle m_handle;
};
class Engine {
public:
Engine(EngineDelegate* delegate);
~Engine();
void printLastResult();
bool eval(const std::string& jsCode,
const std::string& filename = std::string());
bool evalFile(const std::string& filename);
Context& context() { return m_ctx; }
protected:
virtual void onBeforeEval() { }
virtual void onAfterEval(bool err) { }
private:
Context m_ctx;
EngineDelegate* m_delegate;
bool m_printLastResult;
};
}
#endif

View File

@ -1,22 +0,0 @@
// Aseprite Scripting Library
// Copyright (c) 2015-2016 David Capello
//
// 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 "script/engine_delegate.h"
#include <cstdio>
namespace script {
void StdoutEngineDelegate::onConsolePrint(const char* text)
{
std::printf("%s\n", text);
}
} // namespace script

View File

@ -1,26 +0,0 @@
// Aseprite Scripting Library
// Copyright (c) 2015-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef SCRIPT_ENGINE_DELEGATE_H_INCLUDED
#define SCRIPT_ENGINE_DELEGATE_H_INCLUDED
#pragma once
namespace script {
class EngineDelegate {
public:
virtual ~EngineDelegate() { }
virtual void onConsolePrint(const char* text) = 0;
};
class StdoutEngineDelegate : public EngineDelegate {
public:
void onConsolePrint(const char* text) override;
};
}
#endif

View File

@ -134,10 +134,6 @@ public:
allegro_exit(); allegro_exit();
} }
void dispose() override {
delete this;
}
void activateApp() override { void activateApp() override {
// Do nothing // Do nothing
} }

View File

@ -49,6 +49,7 @@ public:
} }
void dispose() override { void dispose() override {
set_instance(nullptr);
delete this; delete this;
} }

View File

@ -28,4 +28,9 @@ System* instance()
return g_system; return g_system;
} }
void set_instance(System* system)
{
g_system = system;
}
} // namespace she } // namespace she

View File

@ -82,6 +82,7 @@ namespace she {
System* create_system(); System* create_system();
System* instance(); System* instance();
void set_instance(System* system);
} // namespace she } // namespace she

View File

@ -140,36 +140,47 @@ if(ENABLE_BENCHMARKS)
add_subdirectory(benchmark) add_subdirectory(benchmark)
endif() endif()
# mujs
add_library(mujs
mujs/jsarray.c
mujs/jsboolean.c
mujs/jsbuiltin.c
mujs/jscompile.c
mujs/jsdate.c
mujs/jsdtoa.c
mujs/jsdump.c
mujs/jserror.c
mujs/jsfunction.c
mujs/jsgc.c
mujs/jsintern.c
mujs/jslex.c
mujs/jsmath.c
mujs/jsnumber.c
mujs/jsobject.c
mujs/json.c
mujs/jsparse.c
mujs/jsproperty.c
mujs/jsregexp.c
mujs/jsrun.c
mujs/jsstate.c
mujs/jsstring.c
mujs/jsvalue.c
mujs/regexp.c
mujs/utf.c
mujs/utftype.c)
target_include_directories(mujs PUBLIC .)
# tinyexpr # tinyexpr
add_library(tinyexpr tinyexpr/tinyexpr.c) add_library(tinyexpr tinyexpr/tinyexpr.c)
target_include_directories(tinyexpr PUBLIC tinyexpr) target_include_directories(tinyexpr PUBLIC tinyexpr)
# lua
add_library(lua
lua/lapi.c
lua/lcode.c
lua/lctype.c
lua/ldebug.c
lua/ldo.c
lua/ldump.c
lua/lfunc.c
lua/lgc.c
lua/llex.c
lua/lmem.c
lua/lobject.c
lua/lopcodes.c
lua/lparser.c
lua/lstate.c
lua/lstring.c
lua/ltable.c
lua/ltm.c
lua/lundump.c
lua/lvm.c
lua/lzio.c
lua/ltests.c)
add_library(lauxlib lua/lauxlib.c)
add_library(lualib
lua/lbaselib.c
lua/lcorolib.c
lua/ldblib.c
lua/linit.c
lua/liolib.c
lua/lmathlib.c
lua/loadlib.c
lua/loslib.c
lua/lstrlib.c
lua/ltablib.c
lua/lutf8lib.c)
target_include_directories(lua PUBLIC lua)
target_include_directories(lauxlib PUBLIC lua)
target_include_directories(lualib PUBLIC lua)
target_link_libraries(lauxlib lua)

1
third_party/lua vendored Submodule

@ -0,0 +1 @@
Subproject commit f59e6a93c0ad38a27a420e51abf8f13d962446b5

1
third_party/mujs vendored

@ -1 +0,0 @@
Subproject commit 34cb61711fe29934dfa82ab55ea59ed85ae62642