2 * Copyright (C) 2016-2019 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "pbd/error.h"
22 #include "pbd/file_utils.h"
23 #include "pbd/compose.h"
25 #include "ardour/directory_names.h"
26 #include "ardour/filesystem_paths.h"
27 #include "ardour/luascripting.h"
28 #include "ardour/lua_script_params.h"
29 #include "ardour/search_paths.h"
30 #include "ardour/utils.h"
32 #include "lua/luastate.h"
33 #include "LuaBridge/LuaBridge.h"
38 using namespace ARDOUR;
42 LuaScripting* LuaScripting::_instance = 0;
45 LuaScripting::instance ()
48 _instance = new LuaScripting;
53 LuaScripting::LuaScripting ()
65 LuaScripting::~LuaScripting ()
67 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
68 // don't bother, just exit quickly.
81 LuaScripting::refresh (bool run_scan)
83 Glib::Threads::Mutex::Lock lm (_lock);
108 LuaScripting::Sorter::operator() (LuaScriptInfoPtr const a, LuaScriptInfoPtr const b) const {
109 return ARDOUR::cmp_nocase_utf8 (a->name, b->name) < 0;
113 LuaScripting::script_info (const std::string &script) {
114 return scan_script ("", script);
118 LuaScripting::scan ()
120 Glib::Threads::Mutex::Lock lm (_lock);
122 #define CLEAR_OR_NEW(LIST) \
123 if (LIST) { LIST->clear (); } else { LIST = new LuaScriptList (); }
125 CLEAR_OR_NEW (_sl_dsp)
126 CLEAR_OR_NEW (_sl_session)
127 CLEAR_OR_NEW (_sl_hook)
128 CLEAR_OR_NEW (_sl_action)
129 CLEAR_OR_NEW (_sl_snippet)
130 CLEAR_OR_NEW (_sl_setup)
131 CLEAR_OR_NEW (_sl_tracks)
135 vector<string> luascripts;
136 find_files_matching_pattern (luascripts, lua_search_path (), "*.lua");
138 for (vector<string>::iterator i = luascripts.begin(); i != luascripts.end (); ++i) {
139 LuaScriptInfoPtr lsi = scan_script (*i);
141 PBD::info << string_compose (_("Script '%1' has no valid descriptor."), *i) << endmsg;
145 case LuaScriptInfo::DSP:
146 _sl_dsp->push_back(lsi);
148 case LuaScriptInfo::Session:
149 _sl_session->push_back(lsi);
151 case LuaScriptInfo::EditorHook:
152 _sl_hook->push_back(lsi);
154 case LuaScriptInfo::EditorAction:
155 _sl_action->push_back(lsi);
157 case LuaScriptInfo::Snippet:
158 _sl_snippet->push_back(lsi);
160 case LuaScriptInfo::SessionInit:
161 _sl_setup->push_back(lsi);
168 std::sort (_sl_dsp->begin(), _sl_dsp->end(), Sorter());
169 std::sort (_sl_session->begin(), _sl_session->end(), Sorter());
170 std::sort (_sl_hook->begin(), _sl_hook->end(), Sorter());
171 std::sort (_sl_action->begin(), _sl_action->end(), Sorter());
172 std::sort (_sl_snippet->begin(), _sl_snippet->end(), Sorter());
173 std::sort (_sl_setup->begin(), _sl_setup->end(), Sorter());
174 std::sort (_sl_tracks->begin(), _sl_tracks->end(), Sorter());
176 scripts_changed (); /* EMIT SIGNAL */
180 LuaScripting::lua_print (std::string s) {
181 PBD::info << "Lua: " << s << "\n";
185 LuaScripting::scan_script (const std::string &fn, const std::string &sc)
188 if (!(fn.empty() ^ sc.empty())){
189 // give either file OR script
191 return LuaScriptInfoPtr();
194 lua_State* L = lua.getState();
195 lua.Print.connect (&LuaScripting::lua_print);
200 "function ardour (entry)"
201 " ardourluainfo['type'] = assert(entry['type'])"
202 " ardourluainfo['name'] = assert(entry['name'])"
203 " ardourluainfo['category'] = entry['category'] or 'Unknown'"
204 " ardourluainfo['author'] = entry['author'] or 'Unknown'"
205 " ardourluainfo['license'] = entry['license'] or ''"
206 " ardourluainfo['description'] = entry['description'] or ''"
213 err = lua.do_command (sc);
215 err = lua.do_file (fn);
219 cerr << "failed to load lua script\n";
221 return LuaScriptInfoPtr();
223 } catch (...) { // luabridge::LuaException
225 cerr << "failed to parse lua script\n";
227 return LuaScriptInfoPtr();
229 luabridge::LuaRef nfo = luabridge::getGlobal (L, "ardourluainfo");
230 if (nfo.type() != LUA_TTABLE) {
232 cerr << "failed to get ardour{} table from script\n";
234 return LuaScriptInfoPtr();
237 if (nfo["name"].type() != LUA_TSTRING || nfo["type"].type() != LUA_TSTRING) {
239 cerr << "script-type or script-name is not a string\n";
241 return LuaScriptInfoPtr();
244 std::string name = nfo["name"].cast<std::string>();
245 LuaScriptInfo::ScriptType type = LuaScriptInfo::str2type (nfo["type"].cast<std::string>());
247 if (name.empty() || type == LuaScriptInfo::Invalid) {
249 cerr << "invalid script-type or missing script name\n";
251 return LuaScriptInfoPtr();
259 sha1_write (&s, (const uint8_t *) sc.c_str(), sc.size ());
262 std::string script = Glib::file_get_contents (fn);
263 sha1_write (&s, (const uint8_t *) script.c_str(), script.size ());
264 } catch (Glib::FileError const& err) {
265 return LuaScriptInfoPtr();
268 sha1_result_hash (&s, hash);
271 LuaScriptInfoPtr lsi (new LuaScriptInfo (type, name, fn, hash));
273 for (luabridge::Iterator i(nfo); !i.isNil (); ++i) {
274 if (!i.key().isString() || !i.value().isString()) {
275 return LuaScriptInfoPtr();
277 std::string key = i.key().tostring();
278 std::string val = i.value().tostring();
280 if (key == "author") { lsi->author = val; }
281 if (key == "license") { lsi->license = val; }
282 if (key == "description") { lsi->description = val; }
283 if (key == "category") { lsi->category = val; }
287 if (type == LuaScriptInfo::EditorAction) {
289 luabridge::LuaRef lua_rs = luabridge::getGlobal (L, "route_setup");
290 if (lua_rs.isFunction ()) {
291 lsi->subtype |= LuaScriptInfo::RouteSetup;
294 luabridge::LuaRef lua_ss = luabridge::getGlobal (L, "session_setup");
295 if (lua_ss.isFunction ()) {
297 if (lua_ss () == true) {
298 lsi->subtype |= LuaScriptInfo::SessionSetup;
309 LuaScripting::scripts (LuaScriptInfo::ScriptType type) {
311 if (!_sl_dsp || !_sl_session || !_sl_hook || !_sl_action || !_sl_snippet || ! _sl_setup || ! _sl_tracks) {
316 case LuaScriptInfo::DSP:
319 case LuaScriptInfo::Session:
322 case LuaScriptInfo::EditorHook:
325 case LuaScriptInfo::EditorAction:
328 case LuaScriptInfo::Snippet:
331 case LuaScriptInfo::SessionInit:
337 return _empty_script_info; // make some compilers happy
342 LuaScriptInfo::type2str (const ScriptType t) {
344 case LuaScriptInfo::DSP: return "DSP";
345 case LuaScriptInfo::Session: return "Session";
346 case LuaScriptInfo::EditorHook: return "EditorHook";
347 case LuaScriptInfo::EditorAction: return "EditorAction";
348 case LuaScriptInfo::Snippet: return "Snippet";
349 case LuaScriptInfo::SessionInit: return "SessionInit";
350 default: return "Invalid";
354 LuaScriptInfo::ScriptType
355 LuaScriptInfo::str2type (const std::string& str) {
356 const char* type = str.c_str();
357 if (!strcasecmp (type, "DSP")) {return LuaScriptInfo::DSP;}
358 if (!strcasecmp (type, "Session")) {return LuaScriptInfo::Session;}
359 if (!strcasecmp (type, "EditorHook")) {return LuaScriptInfo::EditorHook;}
360 if (!strcasecmp (type, "EditorAction")) {return LuaScriptInfo::EditorAction;}
361 if (!strcasecmp (type, "Snippet")) {return LuaScriptInfo::Snippet;}
362 if (!strcasecmp (type, "SessionInit")) {return LuaScriptInfo::SessionInit;}
363 return LuaScriptInfo::Invalid;
367 LuaScriptParams::script_params (const LuaScriptInfoPtr& lsi, const std::string &pname)
370 return LuaScriptParams::script_params (lsi->path, pname);
374 LuaScriptParams::script_params (const std::string& s, const std::string &pname, bool file)
377 return LuaScriptParams::script_params (lua, s, pname, file);
381 LuaScriptParams::script_params (LuaState& lua, const std::string& s, const std::string &pname, bool file)
383 LuaScriptParamList rv;
385 lua_State* L = lua.getState();
387 lua.do_command ("function ardour () end");
399 luabridge::LuaRef lua_params = luabridge::getGlobal (L, pname.c_str());
400 if (lua_params.isFunction ()) {
401 luabridge::LuaRef params = lua_params ();
402 if (params.isTable ()) {
403 for (luabridge::Iterator i (params); !i.isNil (); ++i) {
404 if (!i.key ().isString ()) { continue; }
405 if (!i.value ().isTable ()) { continue; }
406 if (!i.value ()["title"].isString ()) { continue; }
408 std::string name = i.key ().cast<std::string> ();
409 std::string title = i.value ()["title"].cast<std::string> ();
411 bool optional = false;
412 bool preseeded = false;
414 if (i.value ()["default"].isString ()) {
415 dflt = i.value ()["default"].cast<std::string> ();
417 if (i.value ()["optional"].isBoolean ()) {
418 optional = i.value ()["optional"].cast<bool> ();
420 if (i.value ()["preseeded"].isBoolean ()) {
421 preseeded = i.value ()["preseeded"].cast<bool> ();
423 LuaScriptParamPtr lsspp (new LuaScriptParam(name, title, dflt, optional, preseeded));
424 rv.push_back (lsspp);
432 LuaScriptParams::params_to_ref (luabridge::LuaRef *tbl_args, const LuaScriptParamList& args)
434 assert (tbl_args && (*tbl_args).isTable ());
435 for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
436 if ((*i)->optional && !(*i)->is_set) { continue; }
437 (*tbl_args)[(*i)->name] = (*i)->value;
442 LuaScriptParams::ref_to_params (LuaScriptParamList& args, luabridge::LuaRef *tbl_ref)
444 assert (tbl_ref && (*tbl_ref).isTable ());
445 for (luabridge::Iterator i (*tbl_ref); !i.isNil (); ++i) {
446 if (!i.key ().isString ()) { assert(0); continue; }
447 std::string name = i.key ().cast<std::string> ();
448 std::string value = i.value ().cast<std::string> ();
449 for (LuaScriptParamList::const_iterator ii = args.begin(); ii != args.end(); ++ii) {
450 if ((*ii)->name == name) {
451 (*ii)->value = value;
459 LuaScripting::try_compile (const std::string& script, const LuaScriptParamList& args)
461 const std::string& bytecode = get_factory_bytecode (script);
462 if (bytecode.empty()) {
466 l.Print.connect (&LuaScripting::lua_print);
468 lua_State* L = l.getState();
471 " function checkfactory (b, a)"
472 " assert(type(b) == 'string', 'ByteCode must be string')"
473 " load(b)()" // assigns f
474 " assert(type(f) == 'string', 'Assigned ByteCode must be string')"
475 " local factory = load(f)"
476 " assert(type(factory) == 'function', 'Factory is a not a function')"
477 " local env = _ENV; env.f = nil env.os = nil env.io = nil"
478 " load (string.dump(factory, true), nil, nil, env)(a)"
483 luabridge::LuaRef lua_test = luabridge::getGlobal (L, "checkfactory");
484 l.do_command ("checkfactory = nil"); // hide it.
485 l.do_command ("collectgarbage()");
487 luabridge::LuaRef tbl_arg (luabridge::newTable(L));
488 LuaScriptParams::params_to_ref (&tbl_arg, args);
489 lua_test (bytecode, tbl_arg);
491 } catch (luabridge::LuaException const& e) {
493 cerr << e.what() << "\n";
495 lua_print (e.what());
502 LuaScripting::get_factory_bytecode (const std::string& script, const std::string& ffn, const std::string& fp)
505 l.Print.connect (&LuaScripting::lua_print);
507 lua_State* L = l.getState();
510 " function ardour () end"
512 " function dump_function (f)"
513 " assert(type(f) == 'function', 'Factory is a not a function')"
514 " return string.format(\"" + fp + " = %q\", string.dump(f, true))"
519 luabridge::LuaRef lua_dump = luabridge::getGlobal (L, "dump_function");
520 l.do_command ("dump_function = nil"); // hide it
521 l.do_command (script); // register "factory"
522 luabridge::LuaRef lua_factory = luabridge::getGlobal (L, ffn.c_str());
524 if (lua_factory.isFunction()) {
525 return (lua_dump(lua_factory)).cast<std::string> ();
532 LuaScripting::user_script_dir ()
534 std::string dir = Glib::build_filename (user_config_directory(), lua_dir_name);
535 g_mkdir_with_parents (dir.c_str(), 0744);