extensive changes to PresentationInfo API
[ardour.git] / libs / ardour / luascripting.cc
index d3768c17d50098d2ed064c8036baeac2d773e8fe..25c139d26a7f15e84c764bb672d75e66801ad8a6 100644 (file)
  *
  */
 #include <cstring>
+#include <glibmm.h>
 
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
 #include "pbd/compose.h"
 
+#include "ardour/directory_names.h"
+#include "ardour/filesystem_paths.h"
 #include "ardour/luascripting.h"
+#include "ardour/lua_script_params.h"
 #include "ardour/search_paths.h"
 
 #include "lua/luastate.h"
 #include "LuaBridge/LuaBridge.h"
 
 #include "i18n.h"
+#include "sha1.c"
 
 using namespace ARDOUR;
 using namespace PBD;
@@ -50,6 +55,7 @@ LuaScripting::LuaScripting ()
        , _sl_session (0)
        , _sl_hook (0)
        , _sl_action (0)
+       , _sl_snippet (0)
 {
        ;
 }
@@ -62,35 +68,32 @@ LuaScripting::~LuaScripting ()
                delete _sl_session;
                delete _sl_hook;
                delete _sl_action;
+               delete _sl_snippet;
        }
 }
 
-void
-LuaScripting::check_scan ()
-{
-       if (!_sl_dsp || !_sl_session || !_sl_hook || !_sl_action) {
-               scan ();
-       }
-}
 
 void
-LuaScripting::refresh ()
+LuaScripting::refresh (bool run_scan)
 {
-       Glib::Threads::Mutex::Lock lm (_lock, Glib::Threads::TRY_LOCK);
-
-       if (!lm.locked()) {
-               return;
-       }
+       Glib::Threads::Mutex::Lock lm (_lock);
 
        delete _sl_dsp;
        delete _sl_session;
        delete _sl_hook;
        delete _sl_action;
+       delete _sl_snippet;
 
        _sl_dsp = 0;
        _sl_session = 0;
        _sl_hook = 0;
        _sl_action = 0;
+       _sl_snippet = 0;
+
+       if (run_scan) {
+               lm.release ();
+               scan ();
+       }
 }
 
 struct ScriptSorter {
@@ -99,14 +102,15 @@ struct ScriptSorter {
        }
 };
 
+LuaScriptInfoPtr
+LuaScripting::script_info (const std::string &script) {
+       return scan_script ("", script);
+}
+
 void
 LuaScripting::scan ()
 {
-       Glib::Threads::Mutex::Lock lm (_lock, Glib::Threads::TRY_LOCK);
-
-       if (!lm.locked()) {
-               return;
-       }
+       Glib::Threads::Mutex::Lock lm (_lock);
 
 #define CLEAR_OR_NEW(LIST) \
        if (LIST) { LIST->clear (); } else { LIST = new LuaScriptList (); }
@@ -115,6 +119,7 @@ LuaScripting::scan ()
        CLEAR_OR_NEW (_sl_session)
        CLEAR_OR_NEW (_sl_hook)
        CLEAR_OR_NEW (_sl_action)
+       CLEAR_OR_NEW (_sl_snippet)
 
 #undef CLEAR_OR_NEW
 
@@ -140,6 +145,9 @@ LuaScripting::scan ()
                        case LuaScriptInfo::EditorAction:
                                _sl_action->push_back(lsi);
                                break;
+                       case LuaScriptInfo::Snippet:
+                               _sl_snippet->push_back(lsi);
+                               break;
                        default:
                                break;
                }
@@ -149,7 +157,9 @@ LuaScripting::scan ()
        std::sort (_sl_session->begin(), _sl_session->end(), ScriptSorter());
        std::sort (_sl_hook->begin(), _sl_hook->end(), ScriptSorter());
        std::sort (_sl_action->begin(), _sl_action->end(), ScriptSorter());
+       std::sort (_sl_snippet->begin(), _sl_snippet->end(), ScriptSorter());
 
+       scripts_changed (); /* EMIT SIGNAL */
 }
 
 void
@@ -177,6 +187,7 @@ LuaScripting::scan_script (const std::string &fn, const std::string &sc)
                        "function ardour (entry)"
                        "  ardourluainfo['type'] = assert(entry['type'])"
                        "  ardourluainfo['name'] = assert(entry['name'])"
+                       "  ardourluainfo['category'] = entry['category'] or 'Unknown'"
                        "  ardourluainfo['author'] = entry['author'] or 'Unknown'"
                        "  ardourluainfo['license'] = entry['license'] or ''"
                        "  ardourluainfo['description'] = entry['description'] or ''"
@@ -191,17 +202,29 @@ LuaScripting::scan_script (const std::string &fn, const std::string &sc)
                        err = lua.do_file (fn);
                }
                if (err) {
+#ifndef NDEBUG
+               cerr << "failed to load lua script\n";
+#endif
                        return LuaScriptInfoPtr();
                }
        } catch (...) { // luabridge::LuaException
+#ifndef NDEBUG
+               cerr << "failed to parse lua script\n";
+#endif
                return LuaScriptInfoPtr();
        }
        luabridge::LuaRef nfo = luabridge::getGlobal (L, "ardourluainfo");
        if (nfo.type() != LUA_TTABLE) {
+#ifndef NDEBUG
+               cerr << "failed to get ardour{} table from script\n";
+#endif
                return LuaScriptInfoPtr();
        }
 
        if (nfo["name"].type() != LUA_TSTRING || nfo["type"].type() != LUA_TSTRING) {
+#ifndef NDEBUG
+               cerr << "script-type or script-name is not a string\n";
+#endif
                return LuaScriptInfoPtr();
        }
 
@@ -209,10 +232,30 @@ LuaScripting::scan_script (const std::string &fn, const std::string &sc)
        LuaScriptInfo::ScriptType type = LuaScriptInfo::str2type (nfo["type"].cast<std::string>());
 
        if (name.empty() || type == LuaScriptInfo::Invalid) {
+#ifndef NDEBUG
+               cerr << "invalid script-type or missing script name\n";
+#endif
                return LuaScriptInfoPtr();
        }
 
-       LuaScriptInfoPtr lsi (new LuaScriptInfo (type, name, fn));
+       char hash[41];
+       Sha1Digest s;
+       sha1_init (&s);
+
+       if (fn.empty()) {
+               sha1_write (&s, (const uint8_t *) sc.c_str(), sc.size ());
+       } else {
+               try {
+                       std::string script = Glib::file_get_contents (fn);
+                       sha1_write (&s, (const uint8_t *) script.c_str(), script.size ());
+               } catch (Glib::FileError err) {
+                       return LuaScriptInfoPtr();
+               }
+       }
+       sha1_result_hash (&s, hash);
+
+
+       LuaScriptInfoPtr lsi (new LuaScriptInfo (type, name, fn, hash));
 
        for (luabridge::Iterator i(nfo); !i.isNil (); ++i) {
                if (!i.key().isString() || !i.value().isString()) {
@@ -224,14 +267,17 @@ LuaScripting::scan_script (const std::string &fn, const std::string &sc)
                if (key == "author") { lsi->author = val; }
                if (key == "license") { lsi->license = val; }
                if (key == "description") { lsi->description = val; }
+               if (key == "category") { lsi->category = val; }
        }
-
        return lsi;
 }
 
 LuaScriptList &
 LuaScripting::scripts (LuaScriptInfo::ScriptType type) {
-       check_scan();
+
+       if (!_sl_dsp || !_sl_session || !_sl_hook || !_sl_action || !_sl_snippet) {
+               scan ();
+       }
 
        switch (type) {
                case LuaScriptInfo::DSP:
@@ -246,10 +292,13 @@ LuaScripting::scripts (LuaScriptInfo::ScriptType type) {
                case LuaScriptInfo::EditorAction:
                        return *_sl_action;
                        break;
+               case LuaScriptInfo::Snippet:
+                       return *_sl_snippet;
+                       break;
                default:
-                       return _empty_script_info;
                        break;
        }
+       return _empty_script_info; // make some compilers happy
 }
 
 
@@ -260,6 +309,7 @@ LuaScriptInfo::type2str (const ScriptType t) {
                case LuaScriptInfo::Session: return "Session";
                case LuaScriptInfo::EditorHook: return "EditorHook";
                case LuaScriptInfo::EditorAction: return "EditorAction";
+               case LuaScriptInfo::Snippet: return "Snippet";
                default: return "Invalid";
        }
 }
@@ -271,28 +321,38 @@ LuaScriptInfo::str2type (const std::string& str) {
        if (!strcasecmp (type, "Session")) {return LuaScriptInfo::Session;}
        if (!strcasecmp (type, "EditorHook")) {return LuaScriptInfo::EditorHook;}
        if (!strcasecmp (type, "EditorAction")) {return LuaScriptInfo::EditorAction;}
+       if (!strcasecmp (type, "Snippet")) {return LuaScriptInfo::Snippet;}
        return LuaScriptInfo::Invalid;
 }
 
 LuaScriptParamList
-LuaScripting::script_params (LuaScriptInfoPtr lsi, const std::string &fn)
+LuaScriptParams::script_params (LuaScriptInfoPtr lsi, const std::string &pname)
 {
-       LuaScriptParamList rv;
        assert (lsi);
+       return LuaScriptParams::script_params (lsi->path, pname);
+}
+
+LuaScriptParamList
+LuaScriptParams::script_params (const std::string& s, const std::string &pname, bool file)
+{
+       LuaScriptParamList rv;
 
        LuaState lua;
        lua_State* L = lua.getState();
-       lua.Print.connect (&LuaScripting::lua_print);
        lua.do_command ("io = nil;");
        lua.do_command ("function ardour () end");
 
        try {
-               lua.do_file (lsi->path);
+               if (file) {
+                       lua.do_file (s);
+               } else {
+                       lua.do_command (s);
+               }
        } catch (luabridge::LuaException const& e) {
                return rv;
        }
 
-       luabridge::LuaRef lua_params = luabridge::getGlobal (L, fn.c_str());
+       luabridge::LuaRef lua_params = luabridge::getGlobal (L, pname.c_str());
        if (lua_params.isFunction ()) {
                luabridge::LuaRef params = lua_params ();
                if (params.isTable ()) {
@@ -320,6 +380,33 @@ LuaScripting::script_params (LuaScriptInfoPtr lsi, const std::string &fn)
        return rv;
 }
 
+void
+LuaScriptParams::params_to_ref (luabridge::LuaRef *tbl_args, const LuaScriptParamList& args)
+{
+       assert (tbl_args &&  (*tbl_args).isTable ());
+       for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
+               if ((*i)->optional && !(*i)->is_set) { continue; }
+               (*tbl_args)[(*i)->name] = (*i)->value;
+       }
+}
+
+void
+LuaScriptParams::ref_to_params (LuaScriptParamList& args, luabridge::LuaRef *tbl_ref)
+{
+       assert (tbl_ref &&  (*tbl_ref).isTable ());
+       for (luabridge::Iterator i (*tbl_ref); !i.isNil (); ++i) {
+               if (!i.key ().isString ()) { assert(0); continue; }
+               std::string name = i.key ().cast<std::string> ();
+               std::string value = i.value ().cast<std::string> ();
+               for (LuaScriptParamList::const_iterator ii = args.begin(); ii != args.end(); ++ii) {
+                       if ((*ii)->name == name) {
+                               (*ii)->value = value;
+                               break;
+                       }
+               }
+       }
+}
+
 bool
 LuaScripting::try_compile (const std::string& script, const LuaScriptParamList& args)
 {
@@ -328,15 +415,18 @@ LuaScripting::try_compile (const std::string& script, const LuaScriptParamList&
                return false;
        }
        LuaState l;
+       l.Print.connect (&LuaScripting::lua_print);
        lua_State* L = l.getState();
 
        l.do_command (""
                        " function checkfactory (b, a)"
                        "  assert(type(b) == 'string', 'ByteCode must be string')"
-                       "  load(b)()"
+                       "  load(b)()" // assigns f
                        "  assert(type(f) == 'string', 'Assigned ByteCode must be string')"
+                       "  local factory = load(f)"
+                       "  assert(type(factory) == 'function', 'Factory is a not a function')"
                        "  local env = _ENV;  env.f = nil env.debug = nil os.exit = nil"
-                       "  load (string.dump(f, true), nil, nil, env)(a)"
+                       "  load (string.dump(factory, true), nil, nil, env)(a)"
                        " end"
                        );
 
@@ -344,9 +434,17 @@ LuaScripting::try_compile (const std::string& script, const LuaScriptParamList&
                luabridge::LuaRef lua_test = luabridge::getGlobal (L, "checkfactory");
                l.do_command ("checkfactory = nil"); // hide it.
                l.do_command ("collectgarbage()");
-               lua_test (bytecode);
+
+               luabridge::LuaRef tbl_arg (luabridge::newTable(L));
+               LuaScriptParams::params_to_ref (&tbl_arg, args);
+               lua_test (bytecode, tbl_arg);
                return true; // OK
-       } catch (luabridge::LuaException const& e) { }
+       } catch (luabridge::LuaException const& e) {
+#ifndef NDEBUG
+               cerr << e.what() << "\n";
+#endif
+               lua_print (e.what());
+       }
 
        return false;
 }
@@ -379,3 +477,11 @@ LuaScripting::get_factory_bytecode (const std::string& script)
        } catch (luabridge::LuaException const& e) { }
        return "";
 }
+
+std::string
+LuaScripting::user_script_dir ()
+{
+       std::string dir = Glib::build_filename (user_config_directory(), lua_dir_name);
+       g_mkdir_with_parents (dir.c_str(), 0744);
+       return dir;
+}