Make Automation4::Script mostly pure abstract and clean up the implementation of LuaScript

Originally committed to SVN as r5640.
This commit is contained in:
Thomas Goyne 2011-09-28 19:48:37 +00:00
parent c09259c93d
commit 492a0d3046
4 changed files with 181 additions and 369 deletions

View file

@ -36,6 +36,8 @@
#include "config.h"
#include "auto4_base.h"
#ifndef AGI_PRE
#ifdef __WINDOWS__
#include <tchar.h>
@ -60,7 +62,6 @@
#include "ass_file.h"
#include "ass_style.h"
#include "auto4_base.h"
#include "compat.h"
#include "dialog_progress.h"
#include "include/aegisub/context.h"
@ -497,20 +498,9 @@ namespace Automation4 {
impl->Run(bind(progress_sink_wrapper, task, std::tr1::placeholders::_1, this), prio);
}
// Script
/// @brief DOCME
/// @param _filename
///
Script::Script(const wxString &_filename)
: filename(_filename)
, name("")
, description("")
, author("")
, version("")
, loaded(false)
Script::Script(wxString const& filename)
: filename(filename)
{
// copied from auto3
include_path.clear();
@ -526,90 +516,6 @@ namespace Automation4 {
}
}
/// @brief DOCME
///
Script::~Script()
{
for (std::vector<Feature*>::iterator f = features.begin(); f != features.end(); ++f) {
delete *f;
}
}
/// @brief DOCME
/// @return
///
wxString Script::GetPrettyFilename() const
{
wxFileName fn(filename);
return fn.GetFullName();
}
/// @brief DOCME
/// @return
///
const wxString& Script::GetFilename() const
{
return filename;
}
/// @brief DOCME
/// @return
///
const wxString& Script::GetName() const
{
return name;
}
/// @brief DOCME
/// @return
///
const wxString& Script::GetDescription() const
{
return description;
}
/// @brief DOCME
/// @return
///
const wxString& Script::GetAuthor() const
{
return author;
}
/// @brief DOCME
/// @return
///
const wxString& Script::GetVersion() const
{
return version;
}
/// @brief DOCME
/// @return
///
bool Script::GetLoadedState() const
{
return loaded;
}
/// @brief DOCME
/// @return
///
std::vector<Feature*>& Script::GetFeatures()
{
return features;
}
// ScriptManager
@ -923,18 +829,8 @@ namespace Automation4 {
// UnknownScript
/// @brief DOCME
/// @param filename
///
UnknownScript::UnknownScript(const wxString &filename)
: Script(filename)
UnknownScript::UnknownScript(wxString const& filename)
: Script(filename)
{
wxFileName fn(filename);
name = fn.GetName();
description = _("File was not recognized as a script");
loaded = false;
}
};

View file

@ -42,6 +42,7 @@
#include <vector>
#include <wx/dialog.h>
#include <wx/filename.h>
#include <wx/gauge.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
@ -312,64 +313,40 @@ namespace Automation4 {
ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr);
};
/// DOCME
/// @class Script
/// @brief DOCME
///
/// DOCME
class Script {
private:
/// DOCME
wxString filename;
protected:
/// DOCME
wxString name;
/// DOCME
wxString description;
/// DOCME
wxString author;
/// DOCME
wxString version;
/// DOCME
bool loaded; // is the script properly loaded?
/// DOCME
/// The automation include path, consisting of the user-specified paths
/// along with the script's path
wxPathList include_path;
/// DOCME
std::vector<Feature*> features;
Script(const wxString &_filename);
Script(wxString const& filename);
public:
virtual ~Script();
virtual ~Script() { }
/// Reload this script
virtual void Reload() = 0;
const wxString& GetFilename() const;
wxString GetPrettyFilename() const;
const wxString& GetName() const;
const wxString& GetDescription() const;
const wxString& GetAuthor() const;
const wxString& GetVersion() const;
bool GetLoadedState() const;
/// The script's file name with path
wxString GetFilename() const { return filename; }
/// The script's file name without path
wxString GetPrettyFilename() const { return wxFileName(filename).GetFullName(); }
/// The script's name. Not required to be unique.
virtual wxString GetName() const=0;
/// A short description of the script
virtual wxString GetDescription() const=0;
/// The author of the script
virtual wxString GetAuthor() const=0;
/// A version string that should not be used for anything but display
virtual wxString GetVersion() const=0;
/// Did the script load correctly?
virtual bool GetLoadedState() const=0;
std::vector<Feature*>& GetFeatures();
/// Get a list of features provided by this script
virtual std::vector<Feature*> GetFeatures() const=0;
};
/// DOCME
/// @class ScriptManager
/// @brief DOCME
@ -477,11 +454,16 @@ namespace Automation4 {
/// automation engines
class UnknownScript : public Script {
public:
UnknownScript(const wxString &filename);
UnknownScript(wxString const& filename);
/// @brief DOCME
///
void Reload() { };
void Reload() { }
wxString GetName() const { return wxFileName(GetFilename()).GetName(); }
wxString GetDescription() const { return _("File was not recognized as a script"); }
wxString GetAuthor() const { return ""; }
wxString GetVersion() const { return ""; }
bool GetLoadedState() const { return false; }
std::vector<Feature*> GetFeatures() const { return std::vector<Feature*>(); }
};
};

View file

@ -38,8 +38,10 @@
#ifdef WITH_AUTO4_LUA
#include "auto4_lua.h"
#ifndef AGI_PRE
#include <assert.h>
#include <cassert>
#include <algorithm>
@ -52,17 +54,18 @@
#endif
#include <libaegisub/log.h>
#include <libaegisub/scoped_ptr.h>
#include "ass_dialogue.h"
#include "ass_file.h"
#include "ass_override.h"
#include "ass_style.h"
#include "auto4_lua.h"
#include "auto4_lua_factory.h"
#include "auto4_lua_scriptreader.h"
#include "main.h"
#include "standard_paths.h"
#include "text_file_reader.h"
#include "utils.h"
#include "video_context.h"
// This must be below the headers above.
@ -74,13 +77,38 @@
#include <lauxlib.h>
#endif
namespace {
void push_value(lua_State *L, lua_CFunction fn) {
lua_pushcfunction(L, fn);
}
void push_value(lua_State *L, int n) {
lua_pushinteger(L, n);
}
void push_value(lua_State *L, double n) {
lua_pushnumber(L, n);
}
template<class T>
void set_field(lua_State *L, const char *name, T value) {
push_value(L, value);
lua_setfield(L, -2, name);
}
wxString get_global_string(lua_State *L, const char *name) {
lua_getglobal(L, name);
wxString ret;
if (lua_isstring(L, -1))
ret = wxString(lua_tostring(L, -1), wxConvUTF8);
lua_pop(L, 1);
return ret;
}
}
/// DOCME
namespace Automation4 {
// LuaStackcheck
//#ifdef _DEBUG
#if 0
struct LuaStackcheck {
lua_State *L;
@ -140,35 +168,22 @@ namespace Automation4 {
// LuaScript
/// @brief DOCME
/// @param filename
///
LuaScript::LuaScript(const wxString &filename)
: Script(filename)
, L(0)
LuaScript::LuaScript(wxString const& filename)
: Script(filename)
, L(0)
{
Create();
}
/// @brief DOCME
///
LuaScript::~LuaScript()
{
if (L) Destroy();
Destroy();
}
/// @brief DOCME
///
void LuaScript::Create()
{
Destroy();
loaded = true;
try {
// create lua environment
L = lua_open();
@ -209,7 +224,7 @@ namespace Automation4 {
lua_settable(L, -3);
// Replace the default lua module loader with our utf-8 compatible one
// Replace the default lua module loader with our unicode compatible one
lua_getfield(L, -1, "loaders");
lua_pushcfunction(L, LuaModuleLoader);
lua_rawseti(L, -2, 2);
@ -229,37 +244,28 @@ namespace Automation4 {
// make "aegisub" table
lua_pushstring(L, "aegisub");
lua_newtable(L);
// aegisub.register_macro
lua_pushcfunction(L, LuaFeatureMacro::LuaRegister);
lua_setfield(L, -2, "register_macro");
// aegisub.register_filter
lua_pushcfunction(L, LuaFeatureFilter::LuaRegister);
lua_setfield(L, -2, "register_filter");
// aegisub.text_extents
lua_pushcfunction(L, LuaTextExtents);
lua_setfield(L, -2, "text_extents");
// VFR handling
lua_pushcfunction(L, LuaFrameFromMs);
lua_setfield(L, -2, "frame_from_ms");
lua_pushcfunction(L, LuaMsFromFrame);
lua_setfield(L, -2, "ms_from_frame");
lua_pushcfunction(L, LuaVideoSize);
lua_setfield(L, -2, "video_size");
// aegisub.lua_automation_version
lua_pushinteger(L, 4);
lua_setfield(L, -2, "lua_automation_version");
set_field(L, "register_macro", LuaFeatureMacro::LuaRegister);
set_field(L, "register_filter", LuaFeatureFilter::LuaRegister);
set_field(L, "text_extents", LuaTextExtents);
set_field(L, "frame_from_ms", LuaFrameFromMs);
set_field(L, "ms_from_frame", LuaMsFromFrame);
set_field(L, "video_size", LuaVideoSize);
set_field(L, "lua_automation_version", 4);
// store aegisub table to globals
lua_settable(L, LUA_GLOBALSINDEX);
_stackcheck.check_stack(0);
// load user script
LuaScriptReader script_reader(GetFilename());
if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().mb_str(wxConvUTF8))) {
if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().utf8_str())) {
wxString err(lua_tostring(L, -1), wxConvUTF8);
err.Prepend("Error loading Lua script \"" + GetPrettyFilename() + "\":\n\n");
throw ScriptLoadError(STD_STR(err));
}
_stackcheck.check_stack(1);
// and execute it
// this is where features are registered
// don't thread this, as there's no point in it and it seems to break on wx 2.8.3, for some reason
@ -270,80 +276,49 @@ namespace Automation4 {
throw ScriptLoadError(STD_STR(err));
}
_stackcheck.check_stack(0);
lua_getglobal(L, "version");
if (lua_isnumber(L, -1)) {
if (lua_tointeger(L, -1) == 3) {
lua_pop(L, 1); // just to avoid tripping the stackcheck in debug
if (lua_isnumber(L, -1) && lua_tointeger(L, -1) == 3) {
lua_pop(L, 1); // just to avoid tripping the stackcheck in debug
throw ScriptLoadError("Attempted to load an Automation 3 script as an Automation 4 Lua script. Automation 3 is no longer supported.");
}
}
lua_getglobal(L, "script_name");
if (lua_isstring(L, -1)) {
name = wxString(lua_tostring(L, -1), wxConvUTF8);
} else {
name = get_global_string(L, "script_name");
description = get_global_string(L, "script_description");
author = get_global_string(L, "script_author");
version = get_global_string(L, "script_version");
if (name.empty())
name = GetPrettyFilename();
}
lua_getglobal(L, "script_description");
if (lua_isstring(L, -1)) {
description = wxString(lua_tostring(L, -1), wxConvUTF8);
}
lua_getglobal(L, "script_author");
if (lua_isstring(L, -1)) {
author = wxString(lua_tostring(L, -1), wxConvUTF8);
}
lua_getglobal(L, "script_version");
if (lua_isstring(L, -1)) {
version = wxString(lua_tostring(L, -1), wxConvUTF8);
}
lua_pop(L, 5);
lua_pop(L, 1);
// if we got this far, the script should be ready
_stackcheck.check_stack(0);
}
catch (agi::Exception const& e) {
Destroy();
loaded = false;
name = GetPrettyFilename();
description = e.GetChainedMessage();
}
}
}
/// @brief DOCME
/// @return
///
void LuaScript::Destroy()
{
// Assume the script object is clean if there's no Lua state
if (!L) return;
// remove features
for (int i = 0; i < (int)features.size(); i++) {
Feature *f = features[i];
delete f;
}
features.clear();
delete_clear(features);
// delete environment
lua_close(L);
L = 0;
loaded = false;
}
/// @brief DOCME
///
void LuaScript::Reload()
{
Destroy();
Create();
}
/// @brief DOCME
/// @param L
/// @return
///
LuaScript* LuaScript::GetScriptObject(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, "aegisub");
@ -352,41 +327,31 @@ namespace Automation4 {
return (LuaScript*)ptr;
}
int LuaScript::RegisterFeature(Feature *feature) {
features.push_back(feature);
return features.size() - 1;
}
/// @brief DOCME
/// @param L
/// @return
///
int LuaScript::LuaTextExtents(lua_State *L)
{
if (!lua_istable(L, 1)) {
lua_pushstring(L, "First argument to text_extents must be a table");
lua_error(L);
}
if (!lua_isstring(L, 2)) {
lua_pushstring(L, "Second argument to text_extents must be a string");
lua_error(L);
}
if (!lua_istable(L, 1))
return luaL_error(L, "First argument to text_extents must be a table");
if (!lua_isstring(L, 2))
return luaL_error(L, "Second argument to text_extents must be a string");
lua_pushvalue(L, 1);
AssEntry *et = LuaAssFile::LuaToAssEntry(L);
AssStyle *st = dynamic_cast<AssStyle*>(et);
agi::scoped_ptr<AssEntry> et(LuaAssFile::LuaToAssEntry(L));
AssStyle *st = dynamic_cast<AssStyle*>(et.get());
lua_pop(L, 1);
if (!st) {
delete et; // Make sure to delete the "live" pointer
lua_pushstring(L, "Not a style entry");
lua_error(L);
}
if (!st)
return luaL_error(L, "Not a style entry");
wxString text(lua_tostring(L, 2), wxConvUTF8);
double width, height, descent, extlead;
if (!CalculateTextExtents(st, text, width, height, descent, extlead)) {
delete st;
lua_pushstring(L, "Some internal error occurred calculating text_extents");
lua_error(L);
}
delete st;
if (!CalculateTextExtents(st, text, width, height, descent, extlead))
return luaL_error(L, "Some internal error occurred calculating text_extents");
lua_pushnumber(L, width);
lua_pushnumber(L, height);
@ -417,28 +382,20 @@ namespace Automation4 {
if (wxFileName::FileExists(filename)) {
LuaScriptReader script_reader(filename);
if (lua_load(L, script_reader.reader_func, &script_reader, filename.utf8_str())) {
lua_pushfstring(L, "Error loading Lua module \"%s\":\n\n%s", filename.utf8_str().data(), lua_tostring(L, -1));
lua_error(L);
return lua_gettop(L) - pretop;
return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.utf8_str().data(), lua_tostring(L, -1));
}
}
}
return lua_gettop(L) - pretop;
}
/// @brief DOCME
/// @param L
/// @return
///
int LuaScript::LuaInclude(lua_State *L)
{
LuaScript *s = GetScriptObject(L);
if (!lua_isstring(L, 1)) {
lua_pushstring(L, "Argument to include must be a string");
lua_error(L);
return 0;
}
if (!lua_isstring(L, 1))
return luaL_error(L, "Argument to include must be a string");
wxString fnames(lua_tostring(L, 1), wxConvUTF8);
wxFileName fname(fnames);
@ -452,63 +409,42 @@ namespace Automation4 {
} else {
// absolute path, do nothing
}
if (!fname.IsOk() || !fname.FileExists()) {
lua_pushfstring(L, "Lua include not found: %s", fnames.mb_str(wxConvUTF8).data());
lua_error(L);
}
if (!fname.IsOk() || !fname.FileExists())
return luaL_error(L, "Lua include not found: %s", fnames.utf8_str().data());
LuaScriptReader script_reader(fname.GetFullPath());
if (lua_load(L, script_reader.reader_func, &script_reader, fname.GetFullName().mb_str(wxConvUTF8))) {
lua_pushfstring(L, "Error loading Lua include \"%s\":\n\n%s", fname.GetFullPath().mb_str(wxConvUTF8).data(), lua_tostring(L, -1));
lua_error(L);
return 0;
}
if (lua_load(L, script_reader.reader_func, &script_reader, fname.GetFullName().utf8_str()))
return luaL_error(L, "Error loading Lua include \"%s\":\n\n%s", fname.GetFullPath().utf8_str().data(), lua_tostring(L, -1));
int pretop = lua_gettop(L) - 1; // don't count the function value itself
lua_call(L, 0, LUA_MULTRET);
return lua_gettop(L) - pretop;
}
/// @brief DOCME
/// @param L
/// @return
///
int LuaScript::LuaFrameFromMs(lua_State *L)
{
int ms = (int)lua_tonumber(L, -1);
int ms = lua_tointeger(L, -1);
lua_pop(L, 1);
if (VideoContext::Get()->TimecodesLoaded()) {
if (VideoContext::Get()->TimecodesLoaded())
lua_pushnumber(L, VideoContext::Get()->FrameAtTime(ms, agi::vfr::START));
return 1;
} else {
else
lua_pushnil(L);
return 1;
}
}
return 1;
}
/// @brief DOCME
/// @param L
/// @return
///
int LuaScript::LuaMsFromFrame(lua_State *L)
{
int frame = (int)lua_tonumber(L, -1);
int frame = lua_tointeger(L, -1);
lua_pop(L, 1);
if (VideoContext::Get()->TimecodesLoaded()) {
if (VideoContext::Get()->TimecodesLoaded())
lua_pushnumber(L, VideoContext::Get()->TimeAtFrame(frame, agi::vfr::START));
return 1;
} else {
else
lua_pushnil(L);
return 1;
}
}
return 1;
}
/// @brief DOCME
/// @param L
/// @return
///
int LuaScript::LuaVideoSize(lua_State *L)
{
VideoContext *ctx = VideoContext::Get();
@ -529,14 +465,14 @@ namespace Automation4 {
LuaProgressSink lps(L, ps, can_open_config);
if (lua_pcall(L, nargs, nresults, 0)) {
// if the call failed, log the error here
// if the call failed, log the error here
ps->Log("\n\nLua reported a runtime error:\n");
ps->Log(lua_tostring(L, -1));
lua_pop(L, 1);
}
lua_pop(L, 1);
}
lua_gc(L, LUA_GCCOLLECT, 0);
}
}
// LuaThreadedCall
@ -577,10 +513,7 @@ namespace Automation4 {
lua_pop(L, 1);
// add the Feature object
s->features.push_back(this);
// get the index+1 it was pushed into
myid = (int)s->features.size()-1;
myid = s->RegisterFeature(this);
// create table with the functions
// get features table

View file

@ -271,46 +271,8 @@ namespace Automation4 {
void ThrowError();
};
/// DOCME
/// @class LuaScript
/// @brief DOCME
///
/// DOCME
class LuaScript : public Script {
friend class LuaFeature;
private:
/// DOCME
lua_State *L;
void Create(); // load script and create internal structures etc.
void Destroy(); // destroy internal structures, unreg features and delete environment
static LuaScript* GetScriptObject(lua_State *L);
static int LuaTextExtents(lua_State *L);
static int LuaInclude(lua_State *L);
static int LuaModuleLoader(lua_State *L);
static int LuaFrameFromMs(lua_State *L);
static int LuaMsFromFrame(lua_State *L);
static int LuaVideoSize(lua_State *L);
public:
LuaScript(const wxString &filename);
virtual ~LuaScript();
virtual void Reload();
};
void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config);
/// DOCME
/// @class LuaFeatureMacro
/// @brief DOCME
@ -334,8 +296,6 @@ namespace Automation4 {
virtual void Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent);
};
/// DOCME
/// @class LuaFeatureFilter
/// @brief DOCME
@ -367,4 +327,45 @@ namespace Automation4 {
void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
};
class LuaScript : public Script {
lua_State *L;
wxString name;
wxString description;
wxString author;
wxString version;
std::vector<Feature*> features;
/// load script and create internal structures etc.
void Create();
/// destroy internal structures, unreg features and delete environment
void Destroy();
static int LuaTextExtents(lua_State *L);
static int LuaInclude(lua_State *L);
static int LuaModuleLoader(lua_State *L);
static int LuaFrameFromMs(lua_State *L);
static int LuaMsFromFrame(lua_State *L);
static int LuaVideoSize(lua_State *L);
public:
LuaScript(const wxString &filename);
~LuaScript();
static LuaScript* GetScriptObject(lua_State *L);
int RegisterFeature(Feature *feature);
// Script implementation
void Reload();
wxString GetName() const { return name; }
wxString GetDescription() const { return description; }
wxString GetAuthor() const { return author; }
wxString GetVersion() const { return version; }
bool GetLoadedState() const { return L != 0; }
std::vector<Feature*> GetFeatures() const { return features; }
};
};