C++98 compat
[ardour.git] / gtk2_ardour / luainstance.cc
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include "gtkmm2ext/gui_thread.h"
20
21 #include "ardour/audioengine.h"
22 #include "ardour/diskstream.h"
23 #include "ardour/plugin_manager.h"
24 #include "ardour/route.h"
25 #include "ardour/session.h"
26
27 #include "ardour_ui.h"
28 #include "public_editor.h"
29 #include "region_selection.h"
30 #include "luainstance.h"
31 #include "luasignal.h"
32 #include "script_selector.h"
33
34 #include "i18n.h"
35
36 namespace LuaSignal {
37
38 #define STATIC(name,c,p) else if (!strcmp(type, #name)) {return name;}
39 #define SESSION(name,c,p) else if (!strcmp(type, #name)) {return name;}
40 #define ENGINE(name,c,p) else if (!strcmp(type, #name)) {return name;}
41
42 LuaSignal
43 str2luasignal (const std::string &str) {
44         const char* type = str.c_str();
45         if (0) { }
46 #       include "luasignal_syms.h"
47         else {
48                 PBD::fatal << string_compose (_("programming error: %1: %2"), "Impossible LuaSignal type", str) << endmsg;
49                 abort(); /*NOTREACHED*/
50         }
51 }
52 #undef STATIC
53 #undef SESSION
54 #undef ENGINE
55
56 #define STATIC(name,c,p) N_(#name),
57 #define SESSION(name,c,p) N_(#name),
58 #define ENGINE(name,c,p) N_(#name),
59 const char *luasignalstr[] = {
60 #       include "luasignal_syms.h"
61         0
62 };
63
64 #undef STATIC
65 #undef SESSION
66 #undef ENGINE
67 }; // namespace
68
69 ////////////////////////////////////////////////////////////////////////////////
70
71 #define xstr(s) stringify(s)
72 #define stringify(s) #s
73
74 using namespace ARDOUR;
75
76 void
77 LuaInstance::register_hooks (lua_State* L)
78 {
79
80 #define ENGINE(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
81 #define STATIC(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
82 #define SESSION(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
83         luabridge::getGlobalNamespace (L)
84                 .beginNamespace ("LuaSignal")
85 #               include "luasignal_syms.h"
86                 .endNamespace ();
87 #undef ENGINE
88 #undef SESSION
89 #undef STATIC
90
91         luabridge::getGlobalNamespace (L)
92                 .beginNamespace ("LuaSignal")
93                 .beginStdBitSet <LuaSignal::LAST_SIGNAL> ("Set")
94                 .endClass()
95                 .endNamespace ();
96 }
97
98 void
99 LuaInstance::register_classes (lua_State* L)
100 {
101         LuaBindings::stddef (L);
102         LuaBindings::common (L);
103         LuaBindings::session (L);
104
105         register_hooks (L);
106
107         luabridge::getGlobalNamespace (L)
108                 .beginNamespace ("ARDOUR")
109                 .beginClass <RegionSelection> ("RegionSelection")
110                 .addFunction ("clear_all", &RegionSelection::clear_all)
111                 .addFunction ("start", &RegionSelection::start)
112                 .addFunction ("end_frame", &RegionSelection::end_frame)
113                 .addFunction ("n_midi_regions", &RegionSelection::n_midi_regions)
114                 .endClass ()
115
116                 .beginClass <PublicEditor> ("Editor")
117                 .addFunction ("undo", &PublicEditor::undo)
118                 .addFunction ("redo", &PublicEditor::redo)
119                 .addFunction ("play_selection", &PublicEditor::play_selection)
120                 .addFunction ("play_with_preroll", &PublicEditor::play_with_preroll)
121                 .addFunction ("maybe_locate_with_edit_preroll", &PublicEditor::maybe_locate_with_edit_preroll)
122                 .addFunction ("add_location_from_playhead_cursor", &PublicEditor::add_location_from_playhead_cursor)
123                 .addFunction ("goto_nth_marker", &PublicEditor::goto_nth_marker)
124                 .addFunction ("set_show_measures", &PublicEditor::set_show_measures)
125                 .addFunction ("mouse_add_new_marker", &PublicEditor::mouse_add_new_marker)
126                 .addFunction ("split_regions_at", &PublicEditor::split_regions_at)
127                 .addFunction ("maximise_editing_space", &PublicEditor::maximise_editing_space)
128                 .addFunction ("restore_editing_space", &PublicEditor::restore_editing_space)
129                 .addFunction ("get_regions_from_selection_and_mouse", &PublicEditor::get_regions_from_selection_and_mouse)
130                 .addFunction ("show_measures", &PublicEditor::show_measures)
131                 .addFunction ("set_zoom_focus", &PublicEditor::set_zoom_focus)
132                 .addFunction ("do_import", &PublicEditor::do_import)
133                 .addFunction ("do_embed", &PublicEditor::do_embed)
134                 .endClass ()
135                 .endNamespace ();
136
137 #undef ZOOMFOCUS
138 #undef SNAPTYPE
139 #undef SNAPMODE
140 #undef MOUSEMODE
141 #undef DISPLAYCONTROL
142 #undef IMPORTMODE
143 #undef IMPORTPOSITION
144 #undef IMPORTDISPOSITION
145
146 #define ZOOMFOCUS(NAME) .addConst (stringify(NAME), (Editing::ZoomFocus)Editing::NAME)
147 #define SNAPTYPE(NAME) .addConst (stringify(NAME), (Editing::SnapType)Editing::NAME)
148 #define SNAPMODE(NAME) .addConst (stringify(NAME), (Editing::SnapMode)Editing::NAME)
149 #define MOUSEMODE(NAME) .addConst (stringify(NAME), (Editing::MouseMode)Editing::NAME)
150 #define DISPLAYCONTROL(NAME) .addConst (stringify(NAME), (Editing::DisplayControl)Editing::NAME)
151 #define IMPORTMODE(NAME) .addConst (stringify(NAME), (Editing::ImportMode)Editing::NAME)
152 #define IMPORTPOSITION(NAME) .addConst (stringify(NAME), (Editing::ImportPosition)Editing::NAME)
153 #define IMPORTDISPOSITION(NAME) .addConst (stringify(NAME), (Editing::ImportDisposition)Editing::NAME)
154         luabridge::getGlobalNamespace (L)
155                 .beginNamespace ("Editing")
156 #               include "editing_syms.h"
157                 .endNamespace ();
158 }
159
160 #undef xstr
161 #undef stringify
162
163 ////////////////////////////////////////////////////////////////////////////////
164
165 using namespace ARDOUR;
166 using namespace ARDOUR_UI_UTILS;
167 using namespace PBD;
168 using namespace std;
169
170 #ifndef NDEBUG
171 static void _lua_print (std::string s) {
172         std::cout << "LuaInstance: " << s << "\n";
173 }
174 #endif
175
176 LuaInstance* LuaInstance::_instance = 0;
177
178 LuaInstance*
179 LuaInstance::instance ()
180 {
181         if (!_instance) {
182                 _instance  = new LuaInstance;
183         }
184
185         return _instance;
186 }
187
188 LuaInstance::LuaInstance ()
189 {
190 #ifndef NDEBUG
191         lua.Print.connect (&_lua_print);
192 #endif
193         init ();
194
195         LuaScriptParamList args;
196 }
197
198 LuaInstance::~LuaInstance ()
199 {
200         delete _lua_call_action;
201         delete _lua_add_action;
202         delete _lua_del_action;
203         delete _lua_get_action;
204
205         delete _lua_load;
206         delete _lua_save;
207         delete _lua_clear;
208         _callbacks.clear();
209 }
210
211 void
212 LuaInstance::init ()
213 {
214         lua.do_command (
215                         "function ScriptManager ()"
216                         "  local self = { scripts = {}, instances = {} }"
217                         ""
218                         "  local remove = function (id)"
219                         "   self.scripts[id] = nil"
220                         "   self.instances[id] = nil"
221                         "  end"
222                         ""
223                         "  local addinternal = function (i, n, s, f, a)"
224                         "   assert(type(i) == 'number', 'id must be numeric')"
225                         "   assert(type(n) == 'string', 'Name must be string')"
226                         "   assert(type(s) == 'string', 'Script must be string')"
227                         "   assert(type(f) == 'function', 'Factory is a not a function')"
228                         "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
229                         "   self.scripts[i] = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a }"
230                         "   local env = _ENV;  env.f = nil env.debug = nil os.exit = nil require = nil dofile = nil loadfile = nil package = nil"
231                         "   self.instances[i] = load (string.dump(f, true), nil, nil, env)(a)"
232                         "  end"
233                         ""
234                         "  local call = function (id)"
235                         "   if type(self.instances[id]) == 'function' then"
236                         "     local status, err = pcall (self.instances[id])"
237                         "     if not status then"
238                         "       print ('action \"'.. id .. '\": ', err)" // error out
239                         "       remove (id)"
240                         "     end"
241                         "   end"
242                         "   collectgarbage()"
243                         "  end"
244                         ""
245                         "  local add = function (i, n, s, b, a)"
246                         "   assert(type(b) == 'string', 'ByteCode must be string')"
247                         "   load (b)()" // assigns f
248                         "   assert(type(f) == 'string', 'Assigned ByteCode must be string')"
249                         "   addinternal (i, n, s, load(f), a)"
250                         "  end"
251                         ""
252                         "  local get = function (id)"
253                         "   if type(self.scripts[id]) == 'table' then"
254                         "    return { ['name'] = self.scripts[id]['n'],"
255                         "             ['script'] = self.scripts[id]['s'],"
256                         "             ['args'] = self.scripts[id]['a'] }"
257                         "   end"
258                         "   return nil"
259                         "  end"
260                         ""
261                         "  local function basic_serialize (o)"
262                         "    if type(o) == \"number\" then"
263                         "     return tostring(o)"
264                         "    else"
265                         "     return string.format(\"%q\", o)"
266                         "    end"
267                         "  end"
268                         ""
269                         "  local function serialize (name, value)"
270                         "   local rv = name .. ' = '"
271                         "   collectgarbage()"
272                         "   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
273                         "    return rv .. basic_serialize(value) .. ' '"
274                         "   elseif type(value) == \"table\" then"
275                         "    rv = rv .. '{} '"
276                         "    for k,v in pairs(value) do"
277                         "     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
278                         "     rv = rv .. serialize(fieldname, v) .. ' '"
279                         "     collectgarbage()" // string concatenation allocates a new string
280                         "    end"
281                         "    return rv;"
282                         "   elseif type(value) == \"function\" then"
283                         "     return rv .. string.format(\"%q\", string.dump(value, true))"
284                         "   else"
285                         "    error('cannot save a ' .. type(value))"
286                         "   end"
287                         "  end"
288                         ""
289                         ""
290                         "  local save = function ()"
291                         "   return (serialize('scripts', self.scripts))"
292                         "  end"
293                         ""
294                         "  local clear = function ()"
295                         "   self.scripts = {}"
296                         "   self.instances = {}"
297                         "   collectgarbage()"
298                         "  end"
299                         ""
300                         "  local restore = function (state)"
301                         "   clear()"
302                         "   load (state)()"
303                         "   for i, s in pairs (scripts) do"
304                         "    addinternal (i, s['n'], s['s'], load(s['f']), s['a'])"
305                         "   end"
306                         "   collectgarbage()"
307                         "  end"
308                         ""
309                         " return { call = call, add = add, remove = remove, get = get,"
310                         "          restore = restore, save = save, clear = clear}"
311                         " end"
312                         " "
313                         " manager = ScriptManager ()"
314                         " ScriptManager = nil"
315                         );
316
317         lua_State* L = lua.getState();
318
319         try {
320                 luabridge::LuaRef lua_mgr = luabridge::getGlobal (L, "manager");
321                 lua.do_command ("manager = nil"); // hide it.
322                 lua.do_command ("collectgarbage()");
323
324                 _lua_add_action = new luabridge::LuaRef(lua_mgr["add"]);
325                 _lua_del_action = new luabridge::LuaRef(lua_mgr["remove"]);
326                 _lua_get_action = new luabridge::LuaRef(lua_mgr["get"]);
327                 _lua_call_action = new luabridge::LuaRef(lua_mgr["call"]);
328                 _lua_save = new luabridge::LuaRef(lua_mgr["save"]);
329                 _lua_load = new luabridge::LuaRef(lua_mgr["restore"]);
330                 _lua_clear = new luabridge::LuaRef(lua_mgr["clear"]);
331
332         } catch (luabridge::LuaException const& e) {
333                 fatal << string_compose (_("programming error: %1"),
334                                 X_("Failed to setup Lua action interpreter"))
335                         << endmsg;
336                 abort(); /*NOTREACHED*/
337         }
338
339         register_classes (L);
340
341         luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
342         lua_setglobal (L, "Editor");
343 }
344
345 void LuaInstance::set_session (Session* s)
346 {
347         SessionHandlePtr::set_session (s);
348         if (!_session) {
349                 return;
350         }
351
352         lua_State* L = lua.getState();
353         LuaBindings::set_session (L, _session);
354
355         for (LuaCallbackMap::iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
356                 i->second->set_session (s);
357         }
358 }
359
360 void
361 LuaInstance::session_going_away ()
362 {
363         ENSURE_GUI_THREAD (*this, &LuaInstance::session_going_away);
364         (*_lua_clear)();
365         for (int i = 0; i < 9; ++i) {
366                 ActionChanged (i, ""); /* EMIT SIGNAL */
367         }
368         SessionHandlePtr::session_going_away ();
369         _session = 0;
370
371         lua_State* L = lua.getState();
372         LuaBindings::set_session (L, _session);
373         lua.do_command ("collectgarbage();");
374 }
375
376 int
377 LuaInstance::set_state (const XMLNode& node)
378 {
379         LocaleGuard lg (X_("C"));
380         XMLNode* child;
381
382         if ((child = find_named_node (node, "ActionScript"))) {
383                 for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
384                         if (!(*n)->is_content ()) { continue; }
385                         gsize size;
386                         guchar* buf = g_base64_decode ((*n)->content ().c_str (), &size);
387                         try {
388                                 (*_lua_load)(std::string ((const char*)buf, size));
389                         } catch (luabridge::LuaException const& e) {
390                                 cerr << "LuaException:" << e.what () << endl;
391                         }
392                         for (int i = 0; i < 9; ++i) {
393                                 std::string name;
394                                 if (lua_action_name (i, name)) {
395                                         ActionChanged (i, name); /* EMIT SIGNAL */
396                                 }
397                         }
398                         g_free (buf);
399                 }
400         }
401
402         if ((child = find_named_node (node, "ActionHooks"))) {
403                 for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
404                         try {
405                                 LuaCallbackPtr p (new LuaCallback (_session, *(*n)));
406                                 _callbacks.insert (std::make_pair(p->id(), p));
407                                 p->drop_callback.connect (_slotcon, MISSING_INVALIDATOR, boost::bind (&LuaInstance::unregister_lua_slot, this, p->id()), gui_context());
408                                 SlotChanged (p->id(), p->name(), p->signals()); /* EMIT SIGNAL */
409                         } catch (luabridge::LuaException const& e) {
410                                 cerr << "LuaException:" << e.what () << endl;
411                         }
412                 }
413         }
414
415         return 0;
416 }
417
418 bool
419 LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
420 {
421         std::string title;
422         std::vector<std::string> reg;
423
424         switch (type) {
425                 case LuaScriptInfo::EditorAction:
426                         reg = lua_action_names ();
427                         title = "Add Lua Action";
428                         break;
429                 case LuaScriptInfo::EditorHook:
430                         reg = lua_slot_names ();
431                         title = "Add Lua Callback Hook";
432                         break;
433                 default:
434                         return false;
435         }
436
437         LuaScriptInfoPtr spi;
438         ScriptSelector ss (title, type);
439         switch (ss.run ()) {
440                 case Gtk::RESPONSE_ACCEPT:
441                         spi = ss.script();
442                         break;
443                 default:
444                         return false;
445         }
446
447         std::string script = "";
448
449         try {
450                 script = Glib::file_get_contents (spi->path);
451         } catch (Glib::FileError e) {
452                 string msg = string_compose (_("Cannot read script '%1': %2"), spi->path, e.what());
453                 Gtk::MessageDialog am (msg);
454                 am.run ();
455                 return false;
456         }
457
458         LuaScriptParamList lsp = LuaScripting::script_params (spi, "action_params");
459
460         ScriptParameterDialog spd (_("Set Script Parameters"), spi, reg, lsp);
461         switch (spd.run ()) {
462                 case Gtk::RESPONSE_ACCEPT:
463                         break;
464                 default:
465                         return false;
466         }
467
468         switch (type) {
469                 case LuaScriptInfo::EditorAction:
470                         return set_lua_action (id, spd.name(), script, lsp);
471                         break;
472                 case LuaScriptInfo::EditorHook:
473                         return register_lua_slot (spd.name(), script, lsp);
474                         break;
475                 default:
476                         break;
477         }
478         return false;
479 }
480
481 XMLNode&
482 LuaInstance::get_action_state ()
483 {
484         LocaleGuard lg (X_("C"));
485         std::string saved;
486         {
487                 luabridge::LuaRef savedstate ((*_lua_save)());
488                 saved = savedstate.cast<std::string>();
489         }
490         lua.collect_garbage ();
491
492         gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
493         std::string b64s (b64);
494         g_free (b64);
495
496         XMLNode* script_node = new XMLNode (X_("ActionScript"));
497         script_node->add_property (X_("lua"), LUA_VERSION);
498         script_node->add_content (b64s);
499
500         return *script_node;
501 }
502
503 XMLNode&
504 LuaInstance::get_hook_state ()
505 {
506         XMLNode* script_node = new XMLNode (X_("ActionHooks"));
507         for (LuaCallbackMap::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
508                 script_node->add_child_nocopy (i->second->get_state ());
509         }
510         return *script_node;
511 }
512
513 void
514 LuaInstance::call_action (const int id)
515 {
516         try {
517                 (*_lua_call_action)(id + 1);
518         } catch (luabridge::LuaException const& e) {
519                 cerr << "LuaException:" << e.what () << endl;
520         }
521 }
522
523 bool
524 LuaInstance::set_lua_action (
525                 const int id,
526                 const std::string& name,
527                 const std::string& script,
528                 const LuaScriptParamList& args)
529 {
530         try {
531                 lua_State* L = lua.getState();
532                 // get bytcode of factory-function in a sandbox
533                 // (don't allow scripts to interfere)
534                 const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
535                 luabridge::LuaRef tbl_arg (luabridge::newTable(L));
536                 for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
537                         if ((*i)->optional && !(*i)->is_set) { continue; }
538                         tbl_arg[(*i)->name] = (*i)->value;
539                 }
540                 (*_lua_add_action)(id + 1, name, script, bytecode, tbl_arg);
541                 ActionChanged (id, name); /* EMIT SIGNAL */
542         } catch (luabridge::LuaException const& e) {
543                 cerr << "LuaException:" << e.what () << endl;
544                 return false;
545         }
546         return true;
547 }
548
549 bool
550 LuaInstance::remove_lua_action (const int id)
551 {
552         try {
553                 (*_lua_del_action)(id + 1);
554         } catch (luabridge::LuaException const& e) {
555                 cerr << "LuaException:" << e.what () << endl;
556                 return false;
557         }
558         ActionChanged (id, ""); /* EMIT SIGNAL */
559         return true;
560 }
561
562 bool
563 LuaInstance::lua_action_name (const int id, std::string& rv)
564 {
565         try {
566                 luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
567                 if (ref.isNil()) {
568                         return false;
569                 }
570                 if (ref["name"].isString()) {
571                         rv = ref["name"].cast<std::string>();
572                         return true;
573                 }
574                 return true;
575         } catch (luabridge::LuaException const& e) {
576                 cerr << "LuaException:" << e.what () << endl;
577                 return false;
578         }
579         return false;
580 }
581
582 std::vector<std::string>
583 LuaInstance::lua_action_names ()
584 {
585         std::vector<std::string> rv;
586         for (int i = 0; i < 9; ++i) {
587                 std::string name;
588                 if (lua_action_name (i, name)) {
589                         rv.push_back (name);
590                 }
591         }
592         return rv;
593 }
594
595 bool
596 LuaInstance::lua_action (const int id, std::string& name, std::string& script, LuaScriptParamList& args)
597 {
598         try {
599                 luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
600                 if (ref.isNil()) {
601                         return false;
602                 }
603                 if (!ref["name"].isString()) {
604                         return false;
605                 }
606                 if (!ref["script"].isString()) {
607                         return false;
608                 }
609                 if (!ref["args"].isTable()) {
610                         return false;
611                 }
612                 name = ref["name"].cast<std::string>();
613                 script = ref["script"].cast<std::string>();
614
615                 args.clear();
616                 LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
617                 if (!lsi) {
618                         return false;
619                 }
620                 args = LuaScripting::script_params (lsi, "action_params");
621                 for (luabridge::Iterator i (static_cast<luabridge::LuaRef>(ref["args"])); !i.isNil (); ++i) {
622                         if (!i.key ().isString ()) { assert(0); continue; }
623                         std::string name = i.key ().cast<std::string> ();
624                         std::string value = i.value ().cast<std::string> ();
625                         for (LuaScriptParamList::const_iterator ii = args.begin(); ii != args.end(); ++ii) {
626                                 if ((*ii)->name == name) {
627                                         (*ii)->value = value;
628                                         break;
629                                 }
630                         }
631                 }
632                 return true;
633         } catch (luabridge::LuaException const& e) {
634                 cerr << "LuaException:" << e.what () << endl;
635                 return false;
636         }
637         return false;
638 }
639
640 bool
641 LuaInstance::register_lua_slot (const std::string& name, const std::string& script, const ARDOUR::LuaScriptParamList& args)
642 {
643         /* parse script, get ActionHook(s) from script */
644         ActionHook ah;
645         try {
646                 LuaState l;
647 #ifndef NDEBUG
648                 l.Print.connect (&_lua_print);
649 #endif
650                 lua_State* L = l.getState();
651                 register_hooks (L);
652                 l.do_command ("function ardour () end");
653                 l.do_command (script);
654                 luabridge::LuaRef signals = luabridge::getGlobal (L, "signals");
655                 if (signals.isFunction()) {
656                         ah = signals();
657                 }
658         } catch (luabridge::LuaException const& e) {
659                 cerr << "LuaException:" << e.what () << endl;
660         }
661
662         if (ah.none ()) {
663                 cerr << "Script registered no hooks." << endl;
664                 return false;
665         }
666
667         /* register script w/args, get entry-point / ID */
668
669         try {
670                 LuaCallbackPtr p (new LuaCallback (_session, name, script, ah, args));
671                 _callbacks.insert (std::make_pair(p->id(), p));
672                 p->drop_callback.connect (_slotcon, MISSING_INVALIDATOR, boost::bind (&LuaInstance::unregister_lua_slot, this, p->id()), gui_context());
673                 SlotChanged (p->id(), p->name(), p->signals()); /* EMIT SIGNAL */
674                 return true;
675         } catch (luabridge::LuaException const& e) {
676                 cerr << "LuaException:" << e.what () << endl;
677         }
678         return false;
679 }
680
681 bool
682 LuaInstance::unregister_lua_slot (const PBD::ID& id)
683 {
684         LuaCallbackMap::iterator i = _callbacks.find (id);
685         if (i != _callbacks.end()) {
686                 SlotChanged (id, "", ActionHook()); /* EMIT SIGNAL */
687                 _callbacks.erase (i);
688                 return true;
689         }
690         return false;
691 }
692
693 std::vector<PBD::ID>
694 LuaInstance::lua_slots () const
695 {
696         std::vector<PBD::ID> rv;
697         for (LuaCallbackMap::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
698                 rv.push_back (i->first);
699         }
700         return rv;
701 }
702
703 bool
704 LuaInstance::lua_slot_name (const PBD::ID& id, std::string& name) const
705 {
706         LuaCallbackMap::const_iterator i = _callbacks.find (id);
707         if (i != _callbacks.end()) {
708                 name = i->second->name();
709                 return true;
710         }
711         return false;
712 }
713
714 std::vector<std::string>
715 LuaInstance::lua_slot_names () const
716 {
717         std::vector<std::string> rv;
718         std::vector<PBD::ID> ids = lua_slots();
719         for (std::vector<PBD::ID>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
720                 std::string name;
721                 if (lua_slot_name (*i, name)) {
722                         rv.push_back (name);
723                 }
724         }
725         return rv;
726 }
727
728 bool
729 LuaInstance::lua_slot (const PBD::ID& id, std::string& name, std::string& script, ActionHook& ah, ARDOUR::LuaScriptParamList& args)
730 {
731         LuaCallbackMap::const_iterator i = _callbacks.find (id);
732         if (i == _callbacks.end()) {
733                 return false; // error
734         }
735         return i->second->lua_slot (name, script, ah, args);
736 }
737
738 ///////////////////////////////////////////////////////////////////////////////
739
740 LuaCallback::LuaCallback (Session *s,
741                 const std::string& name,
742                 const std::string& script,
743                 const ActionHook& ah,
744                 const ARDOUR::LuaScriptParamList& args)
745         : SessionHandlePtr (s)
746         , _id ("0")
747         , _name (name)
748         , _signals (ah)
749 {
750         // TODO: allow to reference object (e.g region)
751         init ();
752
753         lua_State* L = lua.getState();
754         luabridge::LuaRef tbl_arg (luabridge::newTable(L));
755         for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
756                 if ((*i)->optional && !(*i)->is_set) { continue; }
757                 tbl_arg[(*i)->name] = (*i)->value;
758         }
759
760         try {
761         const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
762         (*_lua_add)(name, script, bytecode, tbl_arg);
763         } catch (luabridge::LuaException const& e) {
764                 cerr << "LuaException:" << e.what () << endl;
765                 throw failed_constructor ();
766         }
767
768         _id.reset ();
769         set_session (s);
770 }
771
772 LuaCallback::LuaCallback (Session *s, XMLNode & node)
773         : SessionHandlePtr (s)
774 {
775         XMLNode* child = NULL;
776         if (node.name() != X_("LuaCallback")
777                         || !node.property ("signals")
778                         || !node.property ("id")
779                         || !node.property ("name")) {
780                 throw failed_constructor ();
781         }
782
783         for (XMLNodeList::const_iterator n = node.children ().begin (); n != node.children ().end (); ++n) {
784                 if (!(*n)->is_content ()) { continue; }
785                 child = *n;
786         }
787
788         if (!child) {
789                 throw failed_constructor ();
790         }
791
792         init ();
793
794         _id = PBD::ID (node.property ("id")->value ());
795         _name = node.property ("name")->value ();
796         _signals = ActionHook (node.property ("signals")->value ());
797
798         gsize size;
799         guchar* buf = g_base64_decode (child->content ().c_str (), &size);
800         try {
801                 (*_lua_load)(std::string ((const char*)buf, size));
802         } catch (luabridge::LuaException const& e) {
803                 cerr << "LuaException:" << e.what () << endl;
804         }
805         g_free (buf);
806
807         set_session (s);
808 }
809
810 LuaCallback::~LuaCallback ()
811 {
812         delete _lua_add;
813         delete _lua_get;
814         delete _lua_call;
815         delete _lua_load;
816         delete _lua_save;
817 }
818
819 XMLNode&
820 LuaCallback::get_state (void)
821 {
822         std::string saved;
823         {
824                 luabridge::LuaRef savedstate ((*_lua_save)());
825                 saved = savedstate.cast<std::string>();
826         }
827         lua.collect_garbage ();
828
829         gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
830         std::string b64s (b64);
831         g_free (b64);
832
833         XMLNode* script_node = new XMLNode (X_("LuaCallback"));
834         script_node->add_property (X_("lua"), LUA_VERSION);
835         script_node->add_property (X_("id"), _id.to_s ());
836         script_node->add_property (X_("name"), _name);
837         script_node->add_property (X_("signals"), _signals.to_string ());
838         script_node->add_content (b64s);
839         return *script_node;
840 }
841
842 void
843 LuaCallback::init (void)
844 {
845 #ifndef NDEBUG
846         lua.Print.connect (&_lua_print);
847 #endif
848
849         lua.do_command (
850                         "function ScriptManager ()"
851                         "  local self = { script = {}, instance = {} }"
852                         ""
853                         "  local addinternal = function (n, s, f, a)"
854                         "   assert(type(n) == 'string', 'Name must be string')"
855                         "   assert(type(s) == 'string', 'Script must be string')"
856                         "   assert(type(f) == 'function', 'Factory is a not a function')"
857                         "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
858                         "   self.script = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a }"
859                         "   local env = _ENV;  env.f = nil env.debug = nil os.exit = nil require = nil dofile = nil loadfile = nil package = nil"
860                         "   self.instance = load (string.dump(f, true), nil, nil, env)(a)"
861                         "  end"
862                         ""
863                         "  local call = function (...)"
864                         "   if type(self.instance) == 'function' then"
865                         "     local status, err = pcall (self.instance, ...)"
866                         "     if not status then"
867                         "       print ('callback \"'.. self.script['n'] .. '\": ', err)" // error out
868                         "       self.script = nil"
869                         "       self.instance = nil"
870                         "       return false"
871                         "     end"
872                         "   end"
873                         "   collectgarbage()"
874                         "   return true"
875                         "  end"
876                         ""
877                         "  local add = function (n, s, b, a)"
878                         "   assert(type(b) == 'string', 'ByteCode must be string')"
879                         "   load (b)()" // assigns f
880                         "   assert(type(f) == 'string', 'Assigned ByteCode must be string')"
881                         "   addinternal (n, s, load(f), a)"
882                         "  end"
883                         ""
884                         "  local get = function ()"
885                         "   if type(self.instance) == 'function' and type(self.script['n']) == 'string' then"
886                         "    return { ['name'] = self.script['n'],"
887                         "             ['script'] = self.script['s'],"
888                         "             ['args'] = self.script['a'] }"
889                         "   end"
890                         "   return nil"
891                         "  end"
892                         ""
893                         // code dup
894                         ""
895                         "  local function basic_serialize (o)"
896                         "    if type(o) == \"number\" then"
897                         "     return tostring(o)"
898                         "    else"
899                         "     return string.format(\"%q\", o)"
900                         "    end"
901                         "  end"
902                         ""
903                         "  local function serialize (name, value)"
904                         "   local rv = name .. ' = '"
905                         "   collectgarbage()"
906                         "   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
907                         "    return rv .. basic_serialize(value) .. ' '"
908                         "   elseif type(value) == \"table\" then"
909                         "    rv = rv .. '{} '"
910                         "    for k,v in pairs(value) do"
911                         "     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
912                         "     rv = rv .. serialize(fieldname, v) .. ' '"
913                         "     collectgarbage()" // string concatenation allocates a new string
914                         "    end"
915                         "    return rv;"
916                         "   elseif type(value) == \"function\" then"
917                         "     return rv .. string.format(\"%q\", string.dump(value, true))"
918                         "   else"
919                         "    error('cannot save a ' .. type(value))"
920                         "   end"
921                         "  end"
922                         ""
923                         // end code dup
924                         ""
925                         "  local save = function ()"
926                         "   return (serialize('s', self.script))"
927                         "  end"
928                         ""
929                         "  local restore = function (state)"
930                         "   self.script = {}"
931                         "   load (state)()"
932                         "   addinternal (s['n'], s['s'], load(s['f']), s['a'])"
933                         "  end"
934                         ""
935                         " return { call = call, add = add, get = get,"
936                         "          restore = restore, save = save}"
937                         " end"
938                         " "
939                         " manager = ScriptManager ()"
940                         " ScriptManager = nil"
941                         );
942
943         lua_State* L = lua.getState();
944
945         try {
946                 luabridge::LuaRef lua_mgr = luabridge::getGlobal (L, "manager");
947                 lua.do_command ("manager = nil"); // hide it.
948                 lua.do_command ("collectgarbage()");
949
950                 _lua_add = new luabridge::LuaRef(lua_mgr["add"]);
951                 _lua_get = new luabridge::LuaRef(lua_mgr["get"]);
952                 _lua_call = new luabridge::LuaRef(lua_mgr["call"]);
953                 _lua_save = new luabridge::LuaRef(lua_mgr["save"]);
954                 _lua_load = new luabridge::LuaRef(lua_mgr["restore"]);
955
956         } catch (luabridge::LuaException const& e) {
957                 fatal << string_compose (_("programming error: %1"),
958                                 X_("Failed to setup Lua callback interpreter"))
959                         << endmsg;
960                 abort(); /*NOTREACHED*/
961         }
962
963         LuaInstance::register_classes (L);
964
965         luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
966         lua_setglobal (L, "Editor");
967 }
968
969 bool
970 LuaCallback::lua_slot (std::string& name, std::string& script, ActionHook& ah, ARDOUR::LuaScriptParamList& args)
971 {
972         // TODO consolidate w/ LuaInstance::lua_action()
973         try {
974                 luabridge::LuaRef ref = (*_lua_get)();
975                 if (ref.isNil()) {
976                         return false;
977                 }
978                 if (!ref["name"].isString()) {
979                         return false;
980                 }
981                 if (!ref["script"].isString()) {
982                         return false;
983                 }
984                 if (!ref["args"].isTable()) {
985                         return false;
986                 }
987
988                 ah = _signals;
989                 name = ref["name"].cast<std::string> ();
990                 script = ref["script"].cast<std::string> ();
991
992                 args.clear();
993                 LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
994                 if (!lsi) {
995                         return false;
996                 }
997                 args = LuaScripting::script_params (lsi, "action_params");
998                 for (luabridge::Iterator i (static_cast<luabridge::LuaRef>(ref["args"])); !i.isNil (); ++i) {
999                         if (!i.key ().isString ()) { assert(0); continue; }
1000                         std::string name = i.key ().cast<std::string> ();
1001                         std::string value = i.value ().cast<std::string> ();
1002                         for (LuaScriptParamList::const_iterator ii = args.begin(); ii != args.end(); ++ii) {
1003                                 if ((*ii)->name == name) {
1004                                         (*ii)->value = value;
1005                                         break;
1006                                 }
1007                         }
1008                 }
1009                 return true;
1010         } catch (luabridge::LuaException const& e) {
1011                 cerr << "LuaException:" << e.what () << endl;
1012                 return false;
1013         }
1014         return false;
1015 }
1016
1017 void
1018 LuaCallback::set_session (ARDOUR::Session *s)
1019 {
1020         SessionHandlePtr::set_session (s);
1021
1022         if (_session) {
1023                 lua_State* L = lua.getState();
1024                 LuaBindings::set_session (L, _session);
1025         }
1026
1027         reconnect();
1028 }
1029
1030 void
1031 LuaCallback::session_going_away ()
1032 {
1033         ENSURE_GUI_THREAD (*this, &LuaCallback::session_going_away);
1034         lua.do_command ("collectgarbage();");
1035
1036         SessionHandlePtr::session_going_away ();
1037         _session = 0;
1038
1039         drop_callback (); /* EMIT SIGNAL */
1040 }
1041
1042 void
1043 LuaCallback::reconnect ()
1044 {
1045         _connections.drop_connections ();
1046         if ((*_lua_get) ().isNil ()) {
1047                 drop_callback (); /* EMIT SIGNAL */
1048                 return;
1049         }
1050
1051         // TODO pass object which emits the signal (e.g region)
1052         //
1053         // save/load bound objects will be tricky.
1054         // Best idea so far is to save/lookup the PBD::ID
1055         // (either use boost::any indirection or templates for bindable
1056         // object types or a switch statement..)
1057         //
1058         // _session->route_by_id ()
1059         // _session->track_by_diskstream_id ()
1060         // _session->source_by_id ()
1061         // _session->controllable_by_id ()
1062         // _session->processor_by_id ()
1063         // RegionFactory::region_by_id ()
1064         //
1065         // TODO loop over objects (if any)
1066
1067         reconnect_object ((void*)0);
1068 }
1069
1070 template <class T> void
1071 LuaCallback::reconnect_object (T obj)
1072 {
1073         for (uint32_t i = 0; i < LuaSignal::LAST_SIGNAL; ++i) {
1074                 if (_signals[i]) {
1075 #define ENGINE(n,c,p) else if (i == LuaSignal::n) { connect_ ## p (LuaSignal::n, AudioEngine::instance(), &(AudioEngine::instance()->c)); }
1076 #define SESSION(n,c,p) else if (i == LuaSignal::n) { if (_session) { connect_ ## p (LuaSignal::n, _session, &(_session->c)); } }
1077 #define STATIC(n,c,p) else if (i == LuaSignal::n) { connect_ ## p (LuaSignal::n, obj, c); }
1078                         if (0) {}
1079 #                       include "luasignal_syms.h"
1080                         else {
1081                                 PBD::fatal << string_compose (_("programming error: %1: %2"), "Impossible LuaSignal type", i) << endmsg;
1082                                 abort(); /*NOTREACHED*/
1083                         }
1084 #undef ENGINE
1085 #undef SESSION
1086 #undef STATIC
1087                 }
1088         }
1089 }
1090
1091 template <typename T, typename S> void
1092 LuaCallback::connect_0 (enum LuaSignal::LuaSignal ls, T ref, S *signal) {
1093         signal->connect (
1094                         _connections, invalidator (*this),
1095                         boost::bind (&LuaCallback::proxy_0<T>, this, ls, ref),
1096                         gui_context());
1097 }
1098
1099 template <typename T, typename C1> void
1100 LuaCallback::connect_1 (enum LuaSignal::LuaSignal ls, T ref, PBD::Signal1<void, C1> *signal) {
1101         signal->connect (
1102                         _connections, invalidator (*this),
1103                         boost::bind (&LuaCallback::proxy_1<T, C1>, this, ls, ref, _1),
1104                         gui_context());
1105 }
1106
1107 template <typename T, typename C1, typename C2> void
1108 LuaCallback::connect_2 (enum LuaSignal::LuaSignal ls, T ref, PBD::Signal2<void, C1, C2> *signal) {
1109         signal->connect (
1110                         _connections, invalidator (*this),
1111                         boost::bind (&LuaCallback::proxy_2<T, C1, C2>, this, ls, ref, _1, _2),
1112                         gui_context());
1113 }
1114
1115 template <typename T> void
1116 LuaCallback::proxy_0 (enum LuaSignal::LuaSignal ls, T ref) {
1117         if (!(*_lua_call)((int)ls, ref)) { drop_callback (); /* EMIT SIGNAL */}
1118 }
1119
1120 template <typename T, typename C1> void
1121 LuaCallback::proxy_1 (enum LuaSignal::LuaSignal ls, T ref, C1 a1) {
1122         if (!(*_lua_call)((int)ls, ref, a1)) { drop_callback (); /* EMIT SIGNAL */}
1123 }
1124
1125 template <typename T, typename C1, typename C2> void
1126 LuaCallback::proxy_2 (enum LuaSignal::LuaSignal ls, T ref, C1 a1, C2 a2) {
1127         if (!(*_lua_call)((int)ls, ref, a1, a2)) { drop_callback (); /* EMIT SIGNAL */}
1128 }