From 956349f87b1e1c5dd0f64cc575755bf89ebd149d Mon Sep 17 00:00:00 2001 From: David Capello Date: Wed, 6 Apr 2016 19:05:06 -0300 Subject: [PATCH] Add Image class to scripting With this change we introduce SpriteWrap and ImageWrap to keep track of modifications made by the script in one transaction. So we can undo the script action as one simple action. --- data/scripts/white_to_alpha.js | 2 +- src/app/CMakeLists.txt | 3 + src/app/cmd/copy_region.cpp | 8 +- src/app/cmd/copy_region.h | 8 +- src/app/script/app_object.cpp | 20 ++++- src/app/script/app_scripting.cpp | 47 +++++++++++ src/app/script/app_scripting.h | 28 +++++- src/app/script/image_class.cpp | 101 ++++++++++++++++++++++ src/app/script/image_class.h | 20 +++++ src/app/script/image_wrap.cpp | 52 ++++++++++++ src/app/script/image_wrap.h | 39 +++++++++ src/app/script/sprite_class.cpp | 141 +++++++------------------------ src/app/script/sprite_class.h | 4 - src/app/script/sprite_wrap.cpp | 96 +++++++++++++++++++++ src/app/script/sprite_wrap.h | 53 ++++++++++++ src/script/engine.cpp | 35 +++++++- src/script/engine.h | 9 ++ 17 files changed, 539 insertions(+), 127 deletions(-) create mode 100644 src/app/script/image_class.cpp create mode 100644 src/app/script/image_class.h create mode 100644 src/app/script/image_wrap.cpp create mode 100644 src/app/script/image_wrap.h create mode 100644 src/app/script/sprite_wrap.cpp create mode 100644 src/app/script/sprite_wrap.h diff --git a/data/scripts/white_to_alpha.js b/data/scripts/white_to_alpha.js index 81eace42a..b8ab13d9f 100644 --- a/data/scripts/white_to_alpha.js +++ b/data/scripts/white_to_alpha.js @@ -2,7 +2,7 @@ // Copyright (C) 2015-2016 by David Capello var col = app.pixelColor -var img = app.activeSprite +var img = app.activeImage for (y=0; yactiveDocument(); if (doc) - ctx.pushObject(wrap_sprite(doc), "Sprite"); + ctx.pushObject(unwrap_engine(ctx)->wrapSprite(doc), "Sprite"); + else + ctx.pushNull(); + return 1; +} + +script::result_t App_get_activeImage(script::ContextHandle handle) +{ + script::Context ctx(handle); + app::Document* doc = UIContext::instance()->activeDocument(); + if (doc) { + SpriteWrap* wrap = unwrap_engine(ctx)->wrapSprite(doc); + ctx.pushObject(wrap->activeImage(), "Image"); + } else ctx.pushNull(); return 1; @@ -55,6 +70,7 @@ const script::FunctionEntry App_methods[] = { }; const script::PropertyEntry App_props[] = { + { "activeImage", App_get_activeImage, nullptr }, { "activeSprite", App_get_activeSprite, nullptr }, { "pixelColor", App_get_pixelColor, nullptr }, { "version", App_get_version, nullptr }, diff --git a/src/app/script/app_scripting.cpp b/src/app/script/app_scripting.cpp index db8c48b6d..637fb1962 100644 --- a/src/app/script/app_scripting.cpp +++ b/src/app/script/app_scripting.cpp @@ -11,9 +11,13 @@ #include "app/script/app_scripting.h" +#include "app/document.h" #include "app/script/app_object.h" #include "app/script/console_object.h" +#include "app/script/image_class.h" +#include "app/script/image_wrap.h" #include "app/script/sprite_class.h" +#include "app/script/sprite_wrap.h" namespace app { @@ -25,8 +29,51 @@ AppScripting::AppScripting(script::EngineDelegate* delegate) register_console_object(ctx); ctx.pushGlobalObject(); + register_image_class(-1, ctx); register_sprite_class(-1, ctx); + + ctx.pushPointer(this); + ctx.setProp(-2, script::kPtrId); + ctx.pop(); } +SpriteWrap* AppScripting::wrapSprite(app::Document* doc) +{ + auto it = m_sprites.find(doc->id()); + if (it != m_sprites.end()) + return it->second; + else { + auto wrap = new SpriteWrap(doc); + m_sprites[doc->id()] = wrap; + return wrap; + } +} + +void AppScripting::onAfterEval(bool err) +{ + // Commit all transactions + if (!err) { + for (auto& it : m_sprites) + it.second->commit(); + } + destroyWrappers(); +} + +void AppScripting::destroyWrappers() +{ + for (auto& it : m_sprites) + delete it.second; + m_sprites.clear(); +} + +AppScripting* unwrap_engine(script::Context& ctx) +{ + ctx.pushGlobalObject(); + ctx.getProp(-1, script::kPtrId); + void* ptr = ctx.getPointer(-1); + ctx.pop(2); + return (AppScripting*)ptr; +} + } diff --git a/src/app/script/app_scripting.h b/src/app/script/app_scripting.h index ac89a43d4..b44399209 100644 --- a/src/app/script/app_scripting.h +++ b/src/app/script/app_scripting.h @@ -5,19 +5,43 @@ // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. -#ifndef APP_SCRIPT_H_INCLUDED -#define APP_SCRIPT_H_INCLUDED +#ifndef APP_SCRIPTING_H_INCLUDED +#define APP_SCRIPTING_H_INCLUDED #pragma once +#include "doc/object_id.h" #include "script/engine.h" +#include + +namespace doc { + class Image; +} + namespace app { + class Document; + class ImageWrap; + class SpriteWrap; class AppScripting : public script::Engine { + typedef std::map Sprites; + public: AppScripting(script::EngineDelegate* delegate); + + SpriteWrap* wrapSprite(app::Document* doc); + + protected: + void onAfterEval(bool err) override; + + private: + void destroyWrappers(); + + Sprites m_sprites; }; + AppScripting* unwrap_engine(script::Context& ctx); + } // namespace app #endif diff --git a/src/app/script/image_class.cpp b/src/app/script/image_class.cpp new file mode 100644 index 000000000..8401d4e58 --- /dev/null +++ b/src/app/script/image_class.cpp @@ -0,0 +1,101 @@ +// Aseprite +// Copyright (C) 2015-2016 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/script/image_class.h" + +#include "app/script/image_wrap.h" +#include "doc/image.h" + +namespace app { + +namespace { + +script::result_t Image_ctor(script::ContextHandle handle) +{ + return 0; +} + +script::result_t Image_putPixel(script::ContextHandle handle) +{ + script::Context ctx(handle); + int x = ctx.requireInt(0); + int y = ctx.requireInt(1); + doc::color_t color = ctx.requireUInt(2); + + auto wrap = (ImageWrap*)ctx.getThis(); + if (wrap) { + wrap->modifyRegion(gfx::Region(gfx::Rect(x, y, 1, 1))); + wrap->image()->putPixel(x, y, color); + } + + return 0; +} + +script::result_t Image_getPixel(script::ContextHandle handle) +{ + script::Context ctx(handle); + int x = ctx.requireInt(0); + int y = ctx.requireInt(1); + + auto wrap = (ImageWrap*)ctx.getThis(); + if (wrap) { + doc::color_t color = wrap->image()->getPixel(x, y); + ctx.pushUInt(color); + return 1; + } + else + return 0; +} + +script::result_t Image_get_width(script::ContextHandle handle) +{ + script::Context ctx(handle); + auto wrap = (ImageWrap*)ctx.getThis(); + if (wrap) { + ctx.pushInt(wrap->image()->width()); + return 1; + } + else + return 0; +} + +script::result_t Image_get_height(script::ContextHandle handle) +{ + script::Context ctx(handle); + auto wrap = (ImageWrap*)ctx.getThis(); + if (wrap) { + ctx.pushInt(wrap->image()->height()); + return 1; + } + else + return 0; +} + +const script::FunctionEntry Image_methods[] = { + { "getPixel", Image_getPixel, 2 }, + { "putPixel", Image_putPixel, 3 }, + { nullptr, nullptr, 0 } +}; + +const script::PropertyEntry Image_props[] = { + { "width", Image_get_width, nullptr }, + { "height", Image_get_height, nullptr }, + { nullptr, nullptr, 0 } +}; + +} // anonymous namespace + +void register_image_class(script::index_t idx, script::Context& ctx) +{ + ctx.registerClass(idx, "Image", Image_ctor, 0, Image_methods, Image_props); +} + +} // namespace app diff --git a/src/app/script/image_class.h b/src/app/script/image_class.h new file mode 100644 index 000000000..4630c7c45 --- /dev/null +++ b/src/app/script/image_class.h @@ -0,0 +1,20 @@ +// Aseprite +// Copyright (C) 2016 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_SCRIPT_IMAGE_CLASS_H_INCLUDED +#define APP_SCRIPT_IMAGE_CLASS_H_INCLUDED +#pragma once + +#include "script/engine.h" + +namespace app { + + void register_image_class(script::index_t idx, script::Context& ctx); + +} // namespace app + +#endif diff --git a/src/app/script/image_wrap.cpp b/src/app/script/image_wrap.cpp new file mode 100644 index 000000000..9b6828afc --- /dev/null +++ b/src/app/script/image_wrap.cpp @@ -0,0 +1,52 @@ +// Aseprite +// Copyright (C) 2016 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/script/image_wrap.h" + +#include "app/cmd/copy_region.h" +#include "app/script/sprite_wrap.h" +#include "app/transaction.h" +#include "doc/image.h" + +namespace app { + +ImageWrap::ImageWrap(SpriteWrap* sprite, doc::Image* img) + : m_sprite(sprite) + , m_image(img) + , m_backup(nullptr) +{ +} + +ImageWrap::~ImageWrap() +{ +} + +void ImageWrap::commit() +{ + if (m_modifiedRegion.isEmpty()) + return; + + sprite()->transaction().execute( + new cmd::CopyRegion(m_image, + m_backup.get(), + m_modifiedRegion, 0, 0, + true)); +} + +void ImageWrap::modifyRegion(const gfx::Region& rgn) +{ + if (!m_backup) + m_backup.reset(doc::Image::createCopy(m_image)); + + m_modifiedRegion |= rgn; +} + +} // namespace app diff --git a/src/app/script/image_wrap.h b/src/app/script/image_wrap.h new file mode 100644 index 000000000..50bc03078 --- /dev/null +++ b/src/app/script/image_wrap.h @@ -0,0 +1,39 @@ +// Aseprite +// Copyright (C) 2016 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_SCRIPT_IMAGE_WRAP_H_INCLUDED +#define APP_SCRIPT_IMAGE_WRAP_H_INCLUDED +#pragma once + +#include "doc/image.h" +#include "doc/image_ref.h" +#include "gfx/region.h" + +namespace app { + class SpriteWrap; + + class ImageWrap { + public: + ImageWrap(SpriteWrap* sprite, doc::Image* img); + ~ImageWrap(); + + void commit(); + + SpriteWrap* sprite() const { return m_sprite; } + doc::Image* image() const { return m_image; } + + void modifyRegion(const gfx::Region& rgn); + private: + SpriteWrap* m_sprite; + doc::Image* m_image; + doc::ImageRef m_backup; + gfx::Region m_modifiedRegion; + }; + +} // namespace app + +#endif diff --git a/src/app/script/sprite_class.cpp b/src/app/script/sprite_class.cpp index 83958616e..786aa1381 100644 --- a/src/app/script/sprite_class.cpp +++ b/src/app/script/sprite_class.cpp @@ -11,8 +11,11 @@ #include "app/script/sprite_class.h" +#include "app/cmd/set_sprite_size.h" #include "app/document.h" #include "app/document_api.h" +#include "app/script/app_scripting.h" +#include "app/script/sprite_wrap.h" #include "app/transaction.h" #include "app/ui/document_view.h" #include "app/ui_context.h" @@ -20,30 +23,10 @@ #include "doc/sprite.h" #include "script/engine.h" -#include -#include - namespace app { namespace { -class SpriteInScript { -public: - SpriteInScript(app::Document* doc) - : m_doc(doc) { - } - - ~SpriteInScript() { - } - - app::Document* document() { - return m_doc; - } - -private: - app::Document* m_doc; -}; - script::result_t Sprite_ctor(script::ContextHandle handle) { script::Context ctx(handle); @@ -58,71 +41,22 @@ script::result_t Sprite_ctor(script::ContextHandle handle) sprite.release(); doc->setContext(UIContext::instance()); - ctx.pushThis(wrap_sprite(doc.release())); + ctx.pushThis(unwrap_engine(ctx)->wrapSprite(doc.release())); } return 0; } -script::result_t Sprite_putPixel(script::ContextHandle handle) -{ - script::Context ctx(handle); - int x = ctx.requireInt(0); - int y = ctx.requireInt(1); - doc::color_t color = ctx.requireUInt(2); - - Document* doc = (Document*)unwrap_sprite(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; -} - -script::result_t Sprite_getPixel(script::ContextHandle handle) -{ - script::Context ctx(handle); - int x = ctx.requireInt(0); - int y = ctx.requireInt(1); - - Document* doc = (Document*)unwrap_sprite(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; -} - script::result_t Sprite_resize(script::ContextHandle handle) { script::Context ctx(handle); int w = ctx.requireInt(0); int h = ctx.requireInt(1); - Document* doc = (Document*)unwrap_sprite(ctx.getThis()); - { - Transaction transaction(UIContext::instance(), "Script Execution", ModifyDocument); - DocumentApi api(doc, transaction); + auto wrap = (SpriteWrap*)ctx.getThis(); + if (wrap) { + Document* doc = wrap->document(); + DocumentApi api(doc, wrap->transaction()); api.setSpriteSize(doc->sprite(), w, h); - transaction.commit(); } return 0; @@ -136,12 +70,11 @@ script::result_t Sprite_crop(script::ContextHandle handle) int w = ctx.requireInt(2); int h = ctx.requireInt(3); - Document* doc = (Document*)unwrap_sprite(ctx.getThis()); - { - Transaction transaction(UIContext::instance(), "Script Execution", ModifyDocument); - DocumentApi api(doc, transaction); + auto wrap = (SpriteWrap*)ctx.getThis(); + if (wrap) { + Document* doc = wrap->document(); + DocumentApi api(doc, wrap->transaction()); api.cropSprite(doc->sprite(), gfx::Rect(x, y, w, h)); - transaction.commit(); } return 0; @@ -150,8 +83,8 @@ script::result_t Sprite_crop(script::ContextHandle handle) script::result_t Sprite_get_width(script::ContextHandle handle) { script::Context ctx(handle); - Document* doc = (Document*)unwrap_sprite(ctx.getThis()); - ctx.pushInt(doc->sprite()->width()); + auto wrap = (SpriteWrap*)ctx.getThis(); + ctx.pushInt(wrap->sprite()->width()); return 1; } @@ -159,16 +92,21 @@ script::result_t Sprite_set_width(script::ContextHandle handle) { script::Context ctx(handle); int w = ctx.requireInt(0); - Document* doc = (Document*)unwrap_sprite(ctx.getThis()); - doc->sprite()->setSize(w, doc->sprite()->height()); + auto wrap = (SpriteWrap*)ctx.getThis(); + + wrap->transaction().execute( + new cmd::SetSpriteSize(wrap->sprite(), + w, + wrap->sprite()->height())); + return 0; } script::result_t Sprite_get_height(script::ContextHandle handle) { script::Context ctx(handle); - Document* doc = (Document*)unwrap_sprite(ctx.getThis()); - ctx.pushInt(doc->sprite()->height()); + auto wrap = (SpriteWrap*)ctx.getThis(); + ctx.pushInt(wrap->sprite()->height()); return 1; } @@ -176,14 +114,17 @@ script::result_t Sprite_set_height(script::ContextHandle handle) { script::Context ctx(handle); int h = ctx.requireInt(0); - Document* doc = (Document*)unwrap_sprite(ctx.getThis()); - doc->sprite()->setSize(doc->sprite()->width(), h); + auto wrap = (SpriteWrap*)ctx.getThis(); + + wrap->transaction().execute( + new cmd::SetSpriteSize(wrap->sprite(), + wrap->sprite()->width(), + h)); + return 0; } const script::FunctionEntry Sprite_methods[] = { - { "getPixel", Sprite_getPixel, 2 }, - { "putPixel", Sprite_putPixel, 3 }, { "resize", Sprite_resize, 2 }, { "crop", Sprite_crop, 4 }, { nullptr, nullptr, 0 } @@ -197,28 +138,6 @@ const script::PropertyEntry Sprite_props[] = { } // anonymous namespace -static std::map g_sprites; - -void* wrap_sprite(app::Document* doc) -{ - auto it = g_sprites.find(doc->id()); - if (it != g_sprites.end()) - return it->second; - else { - SpriteInScript* wrap = new SpriteInScript(doc); - g_sprites[doc->id()] = wrap; - return wrap; - } -} - -app::Document* unwrap_sprite(void* ptr) -{ - if (ptr) - return ((SpriteInScript*)ptr)->document(); - else - return nullptr; -} - void register_sprite_class(script::index_t idx, script::Context& ctx) { ctx.registerClass(idx, "Sprite", Sprite_ctor, 3, Sprite_methods, Sprite_props); diff --git a/src/app/script/sprite_class.h b/src/app/script/sprite_class.h index 98ab6e2d5..5c1ffe038 100644 --- a/src/app/script/sprite_class.h +++ b/src/app/script/sprite_class.h @@ -12,10 +12,6 @@ #include "script/engine.h" namespace app { - class Document; - - void* wrap_sprite(app::Document* doc); - app::Document* unwrap_sprite(void* ptr); void register_sprite_class(script::index_t idx, script::Context& ctx); diff --git a/src/app/script/sprite_wrap.cpp b/src/app/script/sprite_wrap.cpp new file mode 100644 index 000000000..cfdbbef02 --- /dev/null +++ b/src/app/script/sprite_wrap.cpp @@ -0,0 +1,96 @@ +// Aseprite +// Copyright (C) 2016 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/script/sprite_wrap.h" + +#include "app/cmd/set_sprite_size.h" +#include "app/document.h" +#include "app/document_api.h" +#include "app/script/image_wrap.h" +#include "app/transaction.h" +#include "app/ui/document_view.h" +#include "app/ui_context.h" +#include "doc/site.h" +#include "doc/sprite.h" + +namespace app { + +SpriteWrap::SpriteWrap(app::Document* doc) + : m_doc(doc) + , m_view(UIContext::instance()->getFirstDocumentView(m_doc)) + , m_transaction(nullptr) +{ +} + +SpriteWrap::~SpriteWrap() +{ + for (auto it : m_images) + delete it.second; + + if (m_transaction) + delete m_transaction; +} + +Transaction& SpriteWrap::transaction() +{ + if (!m_transaction) { + m_transaction = new Transaction(UIContext::instance(), + "Script Execution", + ModifyDocument); + } + return *m_transaction; +} + +void SpriteWrap::commit() +{ + for (auto it : m_images) + it.second->commit(); + + if (m_transaction) { + m_transaction->commit(); + delete m_transaction; + m_transaction = nullptr; + } +} + +app::Document* SpriteWrap::document() +{ + return m_doc; +} + +doc::Sprite* SpriteWrap::sprite() +{ + return m_doc->sprite(); +} + +ImageWrap* SpriteWrap::activeImage() +{ + if (!m_view) + return nullptr; + + doc::Site site; + m_view->getSite(&site); + return wrapImage(site.image()); +} + +ImageWrap* SpriteWrap::wrapImage(doc::Image* img) +{ + auto it = m_images.find(img->id()); + if (it != m_images.end()) + return it->second; + else { + auto wrap = new ImageWrap(this, img); + m_images[img->id()] = wrap; + return wrap; + } +} + +} // namespace app diff --git a/src/app/script/sprite_wrap.h b/src/app/script/sprite_wrap.h new file mode 100644 index 000000000..5b4f05c37 --- /dev/null +++ b/src/app/script/sprite_wrap.h @@ -0,0 +1,53 @@ +// Aseprite +// Copyright (C) 2016 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_SCRIPT_SPRITE_WRAP_H_INCLUDED +#define APP_SCRIPT_SPRITE_WRAP_H_INCLUDED +#pragma once + +#include "doc/object_id.h" + +#include + +namespace doc { + class Image; + class Sprite; +} + +namespace app { + class Document; + class DocumentView; + class ImageWrap; + class Transaction; + + class SpriteWrap { + typedef std::map Images; + + public: + SpriteWrap(app::Document* doc); + ~SpriteWrap(); + + void commit(); + Transaction& transaction(); + + app::Document* document(); + doc::Sprite* sprite(); + ImageWrap* activeImage(); + + ImageWrap* wrapImage(doc::Image* img); + + private: + app::Document* m_doc; + app::DocumentView* m_view; + app::Transaction* m_transaction; + + Images m_images; + }; + +} // namespace app + +#endif diff --git a/src/script/engine.cpp b/src/script/engine.cpp index bc54660d0..eea3c587c 100644 --- a/src/script/engine.cpp +++ b/src/script/engine.cpp @@ -30,6 +30,8 @@ public: namespace script { +const char* kPtrId = "\xFF" "\xFF" "ptr"; + namespace { // TODO classes in modules isn't supported yet @@ -108,6 +110,11 @@ void Context::pop() duk_pop(m_handle); } +void Context::pop(index_t count) +{ + duk_pop_n(m_handle, count); +} + void Context::remove(index_t idx) { duk_remove(m_handle, idx); @@ -193,6 +200,11 @@ const char* Context::toString(index_t i) return duk_to_string(m_handle, i); } +void* Context::getPointer(index_t i) +{ + return duk_get_pointer(m_handle, i); +} + bool Context::requireBool(index_t i) { return (duk_require_boolean(m_handle, i) ? true: false); @@ -220,7 +232,7 @@ const char* Context::requireString(index_t i) void* Context::requireObject(index_t i, const char* className) { - duk_get_prop_string(m_handle, i, "\xFF" "\xFF" "ptr"); + duk_get_prop_string(m_handle, i, kPtrId); void* result = (void*)duk_to_pointer(m_handle, -1); // TODO check pointer type duk_pop(m_handle); @@ -276,7 +288,12 @@ 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"); + duk_put_prop_string(m_handle, -2, kPtrId); +} + +void Context::pushPointer(void* ptr) +{ + duk_push_pointer(m_handle, ptr); } index_t Context::pushObject() @@ -288,7 +305,7 @@ index_t Context::pushObject(void* ptr, const char* className) { index_t obj = duk_push_object(m_handle); duk_push_pointer(m_handle, ptr); - duk_put_prop_string(m_handle, obj, "\xFF" "\xFF" "ptr"); + duk_put_prop_string(m_handle, obj, kPtrId); // TODO classes in modules isn't supported yet duk_get_global_string(m_handle, className); @@ -395,7 +412,7 @@ void Context::registerClass(index_t idx, void* Context::getThis() { duk_push_this(m_handle); - duk_get_prop_string(m_handle, -1, "\xFF" "\xFF" "ptr"); + duk_get_prop_string(m_handle, -1, kPtrId); void* result = (void*)duk_to_pointer(m_handle, -1); duk_pop_2(m_handle); return result; @@ -445,6 +462,8 @@ void Engine::printLastResult() void Engine::eval(const std::string& jsCode) { + bool errFlag = true; + onBeforeEval(); try { ContextHandle handle = m_ctx.handle(); @@ -453,18 +472,23 @@ void Engine::eval(const std::string& jsCode) if (m_printLastResult && !duk_is_null_or_undefined(handle, -1)) { m_delegate->onConsolePrint(duk_safe_to_string(handle, -1)); + } duk_pop(handle); + errFlag = false; } catch (const std::exception& ex) { std::string err = "Error: "; err += ex.what(); m_delegate->onConsolePrint(err.c_str()); } + onAfterEval(errFlag); } void Engine::evalFile(const std::string& file) { + bool errFlag = true; + onBeforeEval(); try { ContextHandle handle = m_ctx.handle(); @@ -497,14 +521,17 @@ void Engine::evalFile(const std::string& file) if (m_printLastResult && !duk_is_null_or_undefined(handle, -1)) { m_delegate->onConsolePrint(duk_safe_to_string(handle, -1)); + } duk_pop(handle); + errFlag = false; } catch (const std::exception& ex) { std::string err = "Error: "; err += ex.what(); m_delegate->onConsolePrint(err.c_str()); } + onAfterEval(errFlag); } void Engine::registerModule(Module* module) diff --git a/src/script/engine.h b/src/script/engine.h index ec9534a14..fc6e2a515 100644 --- a/src/script/engine.h +++ b/src/script/engine.h @@ -21,6 +21,8 @@ namespace script { typedef struct duk_hthread* ContextHandle; typedef result_t (*Function)(ContextHandle ctx); + extern const char* kPtrId; + struct FunctionEntry { const char* id; Function value; @@ -48,6 +50,7 @@ namespace script { void dump(); void pop(); + void pop(index_t count); void remove(index_t idx); void duplicateTop(); @@ -68,6 +71,7 @@ namespace script { unsigned int getUInt(index_t i); const char* getString(index_t i); const char* toString(index_t i); + void* getPointer(index_t i); void* getThis(); bool hasProp(index_t i, const char* propName); @@ -91,6 +95,7 @@ namespace script { void pushString(const char* str); void pushThis(); void pushThis(void* ptr); + void pushPointer(void* ptr); index_t pushObject(); index_t pushObject(void* ptr, const char* className); void pushGlobalObject(); @@ -134,6 +139,10 @@ namespace script { void registerModule(Module* module); + protected: + virtual void onBeforeEval() { } + virtual void onAfterEval(bool err) { } + private: Context m_ctx; EngineDelegate* m_delegate;