2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (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
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 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/luascripting.h"
26 #include "ardour/lua_script_params.h"
27 #include "ardour/search_paths.h"
29 #include "lua/luastate.h"
30 #include "LuaBridge/LuaBridge.h"
34 using namespace ARDOUR;
38 LuaScripting* LuaScripting::_instance = 0;
41 LuaScripting::instance ()
44 _instance = new LuaScripting;
49 LuaScripting::LuaScripting ()
58 LuaScripting::~LuaScripting ()
60 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
61 // don't bother, just exit quickly.
70 LuaScripting::check_scan ()
72 if (!_sl_dsp || !_sl_session || !_sl_hook || !_sl_action) {
78 LuaScripting::refresh ()
80 Glib::Threads::Mutex::Lock lm (_lock, Glib::Threads::TRY_LOCK);
98 bool operator () (LuaScriptInfoPtr a, LuaScriptInfoPtr b) {
99 return a->name < b->name;
104 LuaScripting::scan ()
106 Glib::Threads::Mutex::Lock lm (_lock, Glib::Threads::TRY_LOCK);
112 #define CLEAR_OR_NEW(LIST) \
113 if (LIST) { LIST->clear (); } else { LIST = new LuaScriptList (); }
115 CLEAR_OR_NEW (_sl_dsp)
116 CLEAR_OR_NEW (_sl_session)
117 CLEAR_OR_NEW (_sl_hook)
118 CLEAR_OR_NEW (_sl_action)
122 vector<string> luascripts;
123 find_files_matching_pattern (luascripts, lua_search_path (), "*.lua");
125 for (vector<string>::iterator i = luascripts.begin(); i != luascripts.end (); ++i) {
126 LuaScriptInfoPtr lsi = scan_script (*i);
128 PBD::info << string_compose (_("Script '%1' has no valid descriptor."), *i) << endmsg;
132 case LuaScriptInfo::DSP:
133 _sl_dsp->push_back(lsi);
135 case LuaScriptInfo::Session:
136 _sl_session->push_back(lsi);
138 case LuaScriptInfo::EditorHook:
139 _sl_hook->push_back(lsi);
141 case LuaScriptInfo::EditorAction:
142 _sl_action->push_back(lsi);
149 std::sort (_sl_dsp->begin(), _sl_dsp->end(), ScriptSorter());
150 std::sort (_sl_session->begin(), _sl_session->end(), ScriptSorter());
151 std::sort (_sl_hook->begin(), _sl_hook->end(), ScriptSorter());
152 std::sort (_sl_action->begin(), _sl_action->end(), ScriptSorter());
157 LuaScripting::lua_print (std::string s) {
158 PBD::info << "Lua: " << s << "\n";
162 LuaScripting::scan_script (const std::string &fn, const std::string &sc)
165 if (!(fn.empty() ^ sc.empty())){
166 // give either file OR script
168 return LuaScriptInfoPtr();
171 lua_State* L = lua.getState();
172 lua.Print.connect (&LuaScripting::lua_print);
174 lua.do_command ("io = nil;");
178 "function ardour (entry)"
179 " ardourluainfo['type'] = assert(entry['type'])"
180 " ardourluainfo['name'] = assert(entry['name'])"
181 " ardourluainfo['author'] = entry['author'] or 'Unknown'"
182 " ardourluainfo['license'] = entry['license'] or ''"
183 " ardourluainfo['description'] = entry['description'] or ''"
190 err = lua.do_command (sc);
192 err = lua.do_file (fn);
195 return LuaScriptInfoPtr();
197 } catch (...) { // luabridge::LuaException
198 return LuaScriptInfoPtr();
200 luabridge::LuaRef nfo = luabridge::getGlobal (L, "ardourluainfo");
201 if (nfo.type() != LUA_TTABLE) {
202 return LuaScriptInfoPtr();
205 if (nfo["name"].type() != LUA_TSTRING || nfo["type"].type() != LUA_TSTRING) {
206 return LuaScriptInfoPtr();
209 std::string name = nfo["name"].cast<std::string>();
210 LuaScriptInfo::ScriptType type = LuaScriptInfo::str2type (nfo["type"].cast<std::string>());
212 if (name.empty() || type == LuaScriptInfo::Invalid) {
213 return LuaScriptInfoPtr();
216 LuaScriptInfoPtr lsi (new LuaScriptInfo (type, name, fn));
218 for (luabridge::Iterator i(nfo); !i.isNil (); ++i) {
219 if (!i.key().isString() || !i.value().isString()) {
220 return LuaScriptInfoPtr();
222 std::string key = i.key().tostring();
223 std::string val = i.value().tostring();
225 if (key == "author") { lsi->author = val; }
226 if (key == "license") { lsi->license = val; }
227 if (key == "description") { lsi->description = val; }
234 LuaScripting::scripts (LuaScriptInfo::ScriptType type) {
238 case LuaScriptInfo::DSP:
241 case LuaScriptInfo::Session:
244 case LuaScriptInfo::EditorHook:
247 case LuaScriptInfo::EditorAction:
251 return _empty_script_info;
258 LuaScriptInfo::type2str (const ScriptType t) {
260 case LuaScriptInfo::DSP: return "DSP";
261 case LuaScriptInfo::Session: return "Session";
262 case LuaScriptInfo::EditorHook: return "EditorHook";
263 case LuaScriptInfo::EditorAction: return "EditorAction";
264 default: return "Invalid";
268 LuaScriptInfo::ScriptType
269 LuaScriptInfo::str2type (const std::string& str) {
270 const char* type = str.c_str();
271 if (!strcasecmp (type, "DSP")) {return LuaScriptInfo::DSP;}
272 if (!strcasecmp (type, "Session")) {return LuaScriptInfo::Session;}
273 if (!strcasecmp (type, "EditorHook")) {return LuaScriptInfo::EditorHook;}
274 if (!strcasecmp (type, "EditorAction")) {return LuaScriptInfo::EditorAction;}
275 return LuaScriptInfo::Invalid;
279 LuaScriptParams::script_params (LuaScriptInfoPtr lsi, const std::string &pname)
282 return LuaScriptParams::script_params (lsi->path, pname);
286 LuaScriptParams::script_params (const std::string& s, const std::string &pname, bool file)
288 LuaScriptParamList rv;
291 lua_State* L = lua.getState();
292 lua.do_command ("io = nil;");
293 lua.do_command ("function ardour () end");
301 } catch (luabridge::LuaException const& e) {
305 luabridge::LuaRef lua_params = luabridge::getGlobal (L, pname.c_str());
306 if (lua_params.isFunction ()) {
307 luabridge::LuaRef params = lua_params ();
308 if (params.isTable ()) {
309 for (luabridge::Iterator i (params); !i.isNil (); ++i) {
310 if (!i.key ().isString ()) { continue; }
311 if (!i.value ().isTable ()) { continue; }
312 if (!i.value ()["title"].isString ()) { continue; }
314 std::string name = i.key ().cast<std::string> ();
315 std::string title = i.value ()["title"].cast<std::string> ();
317 bool optional = false;
319 if (i.value ()["default"].isString ()) {
320 dflt = i.value ()["default"].cast<std::string> ();
322 if (i.value ()["optional"].isBoolean ()) {
323 optional = i.value ()["optional"].cast<bool> ();
325 LuaScriptParamPtr lsspp (new LuaScriptParam(name, title, dflt, optional));
326 rv.push_back (lsspp);
334 LuaScriptParams::params_to_ref (luabridge::LuaRef *tbl_args, const LuaScriptParamList& args)
336 assert (tbl_args && (*tbl_args).isTable ());
337 for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
338 if ((*i)->optional && !(*i)->is_set) { continue; }
339 (*tbl_args)[(*i)->name] = (*i)->value;
344 LuaScriptParams::ref_to_params (LuaScriptParamList& args, luabridge::LuaRef *tbl_ref)
346 assert (tbl_ref && (*tbl_ref).isTable ());
347 for (luabridge::Iterator i (*tbl_ref); !i.isNil (); ++i) {
348 if (!i.key ().isString ()) { assert(0); continue; }
349 std::string name = i.key ().cast<std::string> ();
350 std::string value = i.value ().cast<std::string> ();
351 for (LuaScriptParamList::const_iterator ii = args.begin(); ii != args.end(); ++ii) {
352 if ((*ii)->name == name) {
353 (*ii)->value = value;
361 LuaScripting::try_compile (const std::string& script, const LuaScriptParamList& args)
363 const std::string& bytecode = get_factory_bytecode (script);
364 if (bytecode.empty()) {
368 lua_State* L = l.getState();
371 " function checkfactory (b, a)"
372 " assert(type(b) == 'string', 'ByteCode must be string')"
374 " assert(type(f) == 'string', 'Assigned ByteCode must be string')"
375 " local env = _ENV; env.f = nil env.debug = nil os.exit = nil"
376 " load (string.dump(f, true), nil, nil, env)(a)"
381 luabridge::LuaRef lua_test = luabridge::getGlobal (L, "checkfactory");
382 l.do_command ("checkfactory = nil"); // hide it.
383 l.do_command ("collectgarbage()");
386 } catch (luabridge::LuaException const& e) { }
392 LuaScripting::get_factory_bytecode (const std::string& script)
395 l.Print.connect (&LuaScripting::lua_print);
396 lua_State* L = l.getState();
399 " function ardour () end"
401 " function dump_function (f)"
402 " assert(type(f) == 'function', 'Factory is a not a function')"
403 " return string.format(\"f = %q\", string.dump(f, true))"
408 luabridge::LuaRef lua_dump = luabridge::getGlobal (L, "dump_function");
409 l.do_command ("dump_function = nil"); // hide it
410 l.do_command (script); // register "factory"
411 luabridge::LuaRef lua_factory = luabridge::getGlobal (L, "factory");
413 if (lua_factory.isFunction()) {
414 return (lua_dump(lua_factory)).cast<std::string> ();
416 } catch (luabridge::LuaException const& e) { }