add missing GUI lua binding classe
[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 <cairomm/context.h>
20
21 #include "gtkmm2ext/gui_thread.h"
22
23 #include "ardour/audioengine.h"
24 #include "ardour/diskstream.h"
25 #include "ardour/plugin_manager.h"
26 #include "ardour/route.h"
27 #include "ardour/session.h"
28
29 #include "ardour_ui.h"
30 #include "public_editor.h"
31 #include "region_selection.h"
32 #include "luainstance.h"
33 #include "luasignal.h"
34 #include "script_selector.h"
35
36 #include "i18n.h"
37
38 namespace LuaSignal {
39
40 #define STATIC(name,c,p) else if (!strcmp(type, #name)) {return name;}
41 #define SESSION(name,c,p) else if (!strcmp(type, #name)) {return name;}
42 #define ENGINE(name,c,p) else if (!strcmp(type, #name)) {return name;}
43
44 LuaSignal
45 str2luasignal (const std::string &str) {
46         const char* type = str.c_str();
47         if (0) { }
48 #       include "luasignal_syms.h"
49         else {
50                 PBD::fatal << string_compose (_("programming error: %1: %2"), "Impossible LuaSignal type", str) << endmsg;
51                 abort(); /*NOTREACHED*/
52         }
53 }
54 #undef STATIC
55 #undef SESSION
56 #undef ENGINE
57
58 #define STATIC(name,c,p) N_(#name),
59 #define SESSION(name,c,p) N_(#name),
60 #define ENGINE(name,c,p) N_(#name),
61 const char *luasignalstr[] = {
62 #       include "luasignal_syms.h"
63         0
64 };
65
66 #undef STATIC
67 #undef SESSION
68 #undef ENGINE
69 }; // namespace
70
71 ////////////////////////////////////////////////////////////////////////////////
72
73 #define xstr(s) stringify(s)
74 #define stringify(s) #s
75
76 using namespace ARDOUR;
77
78 void
79 LuaInstance::register_hooks (lua_State* L)
80 {
81
82 #define ENGINE(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
83 #define STATIC(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
84 #define SESSION(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
85         luabridge::getGlobalNamespace (L)
86                 .beginNamespace ("LuaSignal")
87 #               include "luasignal_syms.h"
88                 .endNamespace ();
89 #undef ENGINE
90 #undef SESSION
91 #undef STATIC
92
93         luabridge::getGlobalNamespace (L)
94                 .beginNamespace ("LuaSignal")
95                 .beginStdBitSet <LuaSignal::LAST_SIGNAL> ("Set")
96                 .endClass()
97                 .endNamespace ();
98 }
99
100 void
101 LuaInstance::bind_cairo (lua_State* L)
102 {
103         luabridge::getGlobalNamespace (L)
104                 .beginNamespace ("Cairo")
105                 .beginClass <Cairo::Context> ("Context")
106                 .addFunction ("save", &Cairo::Context::save)
107                 .addFunction ("restore", &Cairo::Context::restore)
108                 .addFunction ("set_operator", &Cairo::Context::set_operator)
109                 //.addFunction ("set_source", &Cairo::Context::set_operator) // needs RefPtr
110                 .addFunction ("set_source_rgb", &Cairo::Context::set_source_rgb)
111                 .addFunction ("set_source_rgba", &Cairo::Context::set_source_rgba)
112                 .addFunction ("set_line_width", &Cairo::Context::set_line_width)
113                 .addFunction ("set_line_cap", &Cairo::Context::set_line_cap)
114                 .addFunction ("set_line_join", &Cairo::Context::set_line_join)
115                 .addFunction ("set_dash", (void (Cairo::Context::*)(std::vector<double>&, double))&Cairo::Context::set_dash)
116                 .addFunction ("unset_dash", &Cairo::Context::unset_dash)
117                 .addFunction ("translate", &Cairo::Context::translate)
118                 .addFunction ("scale", &Cairo::Context::scale)
119                 .addFunction ("rotate", &Cairo::Context::rotate)
120                 .addFunction ("begin_new_path", &Cairo::Context::begin_new_path)
121                 .addFunction ("begin_new_sub_path", &Cairo::Context::begin_new_sub_path)
122                 .addFunction ("move_to", &Cairo::Context::move_to)
123                 .addFunction ("line_to", &Cairo::Context::line_to)
124                 .addFunction ("curve_to", &Cairo::Context::curve_to)
125                 .addFunction ("arc", &Cairo::Context::arc)
126                 .addFunction ("arc_negative", &Cairo::Context::arc_negative)
127                 .addFunction ("rel_move_to", &Cairo::Context::rel_move_to)
128                 .addFunction ("rel_line_to", &Cairo::Context::rel_line_to)
129                 .addFunction ("rel_curve_to", &Cairo::Context::rel_curve_to)
130                 .addFunction ("rectangle", (void (Cairo::Context::*)(double, double, double, double))&Cairo::Context::rectangle)
131                 .addFunction ("close_path", &Cairo::Context::close_path)
132                 .addFunction ("paint", &Cairo::Context::paint)
133                 .addFunction ("paint_with_alpha", &Cairo::Context::paint_with_alpha)
134                 .addFunction ("stroke", &Cairo::Context::stroke)
135                 .addFunction ("stroke_preserve", &Cairo::Context::stroke_preserve)
136                 .addFunction ("fill", &Cairo::Context::fill)
137                 .addFunction ("fill_preserve", &Cairo::Context::fill_preserve)
138                 .addFunction ("reset_clip", &Cairo::Context::reset_clip)
139                 .addFunction ("clip", &Cairo::Context::clip)
140                 .addFunction ("clip_preserve", &Cairo::Context::clip_preserve)
141                 .addFunction ("set_font_size", &Cairo::Context::set_font_size)
142                 .addFunction ("show_text", &Cairo::Context::show_text)
143                 .endClass ()
144                 /* enums */
145                 // LineCap, LineJoin, Operator
146                 .beginNamespace ("LineCap")
147                 .addConst ("Butt", CAIRO_LINE_CAP_BUTT)
148                 .addConst ("Round", CAIRO_LINE_CAP_ROUND)
149                 .addConst ("Square", CAIRO_LINE_CAP_SQUARE)
150                 .endNamespace ()
151
152                 .beginNamespace ("LineJoin")
153                 .addConst ("Miter", CAIRO_LINE_JOIN_MITER)
154                 .addConst ("Round", CAIRO_LINE_JOIN_ROUND)
155                 .addConst ("Bevel", CAIRO_LINE_JOIN_BEVEL)
156                 .endNamespace ()
157
158                 .beginNamespace ("Operator")
159                 .addConst ("Clear", CAIRO_OPERATOR_CLEAR)
160                 .addConst ("Source", CAIRO_OPERATOR_SOURCE)
161                 .addConst ("Over", CAIRO_OPERATOR_OVER)
162                 .addConst ("Add", CAIRO_OPERATOR_ADD)
163                 .endNamespace ()
164
165                 .endNamespace ();
166
167 /* Lua/cairo bindings operate on Cairo::Context, there is no Cairo::RefPtr wrapper [yet].
168   one can work around this as follows:
169
170   LuaState lua;
171   LuaInstance::register_classes (lua.getState());
172   lua.do_command (
173       "function render (ctx)"
174       "  ctx:rectangle (0, 0, 100, 100)"
175       "  ctx:set_source_rgba (0.1, 1.0, 0.1, 1.0)"
176       "  ctx:fill ()"
177       " end"
178       );
179   {
180                 Cairo::RefPtr<Cairo::Context> context = get_window ()->create_cairo_context ();
181     Cairo::Context ctx (context->cobj ());
182
183     luabridge::LuaRef lua_render = luabridge::getGlobal (lua.getState(), "render");
184     lua_render ((Cairo::Context *)&ctx);
185   }
186 */
187
188 }
189
190 void
191 LuaInstance::register_classes (lua_State* L)
192 {
193         LuaBindings::stddef (L);
194         LuaBindings::common (L);
195         LuaBindings::session (L);
196         LuaBindings::osc (L);
197
198         bind_cairo (L);
199         register_hooks (L);
200
201         luabridge::getGlobalNamespace (L)
202                 .beginNamespace ("ARDOUR")
203                 .beginClass <RegionSelection> ("RegionSelection")
204                 .addFunction ("clear_all", &RegionSelection::clear_all)
205                 .addFunction ("start", &RegionSelection::start)
206                 .addFunction ("end_frame", &RegionSelection::end_frame)
207                 .addFunction ("n_midi_regions", &RegionSelection::n_midi_regions)
208                 .endClass ()
209
210                 .beginClass <ArdourMarker> ("ArdourMarker")
211                 .endClass ()
212
213                 .beginClass <PublicEditor> ("Editor")
214                 .addFunction ("snap_type", &PublicEditor::snap_type)
215                 .addFunction ("snap_mode", &PublicEditor::snap_mode)
216                 .addFunction ("set_snap_mode", &PublicEditor::set_snap_mode)
217                 .addFunction ("set_snap_threshold", &PublicEditor::set_snap_threshold)
218
219                 .addFunction ("undo", &PublicEditor::undo)
220                 .addFunction ("redo", &PublicEditor::redo)
221
222                 .addFunction ("set_mouse_mode", &PublicEditor::set_mouse_mode)
223                 .addFunction ("current_mouse_mode", &PublicEditor::current_mouse_mode)
224
225                 .addFunction ("consider_auditioning", &PublicEditor::consider_auditioning)
226
227                 .addFunction ("new_region_from_selection", &PublicEditor::new_region_from_selection)
228                 .addFunction ("separate_region_from_selection", &PublicEditor::separate_region_from_selection)
229                 .addFunction ("pixel_to_sample", &PublicEditor::pixel_to_sample)
230                 .addFunction ("sample_to_pixel", &PublicEditor::sample_to_pixel)
231
232 #if 0 // Selection is not yet exposed
233                 .addFunction ("get_selection", &PublicEditor::get_selection)
234                 .addFunction ("get_cut_buffer", &PublicEditor::get_cut_buffer)
235                 .addFunction ("track_mixer_selection", &PublicEditor::track_mixer_selection)
236                 .addFunction ("extend_selection_to_track", &PublicEditor::extend_selection_to_track)
237 #endif
238
239                 .addFunction ("play_selection", &PublicEditor::play_selection)
240                 .addFunction ("play_with_preroll", &PublicEditor::play_with_preroll)
241                 .addFunction ("maybe_locate_with_edit_preroll", &PublicEditor::maybe_locate_with_edit_preroll)
242                 .addFunction ("goto_nth_marker", &PublicEditor::goto_nth_marker)
243
244                 .addFunction ("add_location_from_playhead_cursor", &PublicEditor::add_location_from_playhead_cursor)
245                 .addFunction ("remove_location_at_playhead_cursor", &PublicEditor::remove_location_at_playhead_cursor)
246
247                 .addFunction ("set_show_measures", &PublicEditor::set_show_measures)
248                 .addFunction ("show_measures", &PublicEditor::show_measures)
249                 .addFunction ("remove_tracks", &PublicEditor::remove_tracks)
250
251                 .addFunction ("effective_mouse_mode", &PublicEditor::effective_mouse_mode)
252
253                 .addFunction ("do_import", &PublicEditor::do_import)
254                 .addFunction ("do_embed", &PublicEditor::do_embed)
255
256                 .addFunction ("export_audio", &PublicEditor::export_audio)
257                 .addFunction ("stem_export", &PublicEditor::stem_export)
258                 .addFunction ("export_selection", &PublicEditor::export_selection)
259                 .addFunction ("export_range", &PublicEditor::export_range)
260
261                 .addFunction ("set_zoom_focus", &PublicEditor::set_zoom_focus)
262                 .addFunction ("get_zoom_focus", &PublicEditor::get_zoom_focus)
263                 .addFunction ("get_current_zoom", &PublicEditor::get_current_zoom)
264                 .addFunction ("reset_zoom", &PublicEditor::reset_zoom)
265
266 #if 0 // These need TimeAxisView* which isn't exposed, yet
267                 .addFunction ("playlist_selector", &PublicEditor::playlist_selector)
268                 .addFunction ("clear_playlist", &PublicEditor::clear_playlist)
269                 .addFunction ("new_playlists", &PublicEditor::new_playlists)
270                 .addFunction ("copy_playlists", &PublicEditor::copy_playlists)
271                 .addFunction ("clear_playlists", &PublicEditor::clear_playlists)
272 #endif
273
274                 .addFunction ("select_all_tracks", &PublicEditor::select_all_tracks)
275                 .addFunction ("deselect_all", &PublicEditor::deselect_all)
276 #if 0
277                 .addFunction ("set_selected_track", &PublicEditor::set_selected_track)
278                 .addFunction ("set_selected_mixer_strip", &PublicEditor::set_selected_mixer_strip)
279                 .addFunction ("hide_track_in_display", &PublicEditor::hide_track_in_display)
280 #endif
281                 .addFunction ("set_stationary_playhead", &PublicEditor::set_stationary_playhead)
282                 .addFunction ("stationary_playhead", &PublicEditor::stationary_playhead)
283                 .addFunction ("set_follow_playhead", &PublicEditor::set_follow_playhead)
284                 .addFunction ("follow_playhead", &PublicEditor::follow_playhead)
285
286                 .addFunction ("dragging_playhead", &PublicEditor::dragging_playhead)
287                 .addFunction ("leftmost_sample", &PublicEditor::leftmost_sample)
288                 .addFunction ("current_page_samples", &PublicEditor::current_page_samples)
289                 .addFunction ("visible_canvas_height", &PublicEditor::visible_canvas_height)
290                 .addFunction ("temporal_zoom_step", &PublicEditor::temporal_zoom_step)
291                 //.addFunction ("ensure_time_axis_view_is_visible", &PublicEditor::ensure_time_axis_view_is_visible)
292                 .addFunction ("override_visible_track_count", &PublicEditor::override_visible_track_count)
293
294                 .addFunction ("scroll_tracks_down_line", &PublicEditor::scroll_tracks_down_line)
295                 .addFunction ("scroll_tracks_up_line", &PublicEditor::scroll_tracks_up_line)
296                 .addFunction ("scroll_down_one_track", &PublicEditor::scroll_down_one_track)
297                 .addFunction ("scroll_up_one_track", &PublicEditor::scroll_up_one_track)
298
299                 .addFunction ("reset_x_origin", &PublicEditor::reset_x_origin)
300                 .addFunction ("get_y_origin", &PublicEditor::get_y_origin)
301                 .addFunction ("reset_y_origin", &PublicEditor::reset_y_origin)
302
303                 .addFunction ("remove_last_capture", &PublicEditor::remove_last_capture)
304
305                 .addFunction ("maximise_editing_space", &PublicEditor::maximise_editing_space)
306                 .addFunction ("restore_editing_space", &PublicEditor::restore_editing_space)
307                 .addFunction ("toggle_meter_updating", &PublicEditor::toggle_meter_updating)
308
309                 //.addFunction ("get_preferred_edit_position", &PublicEditor::get_preferred_edit_position)
310                 //.addFunction ("split_regions_at", &PublicEditor::split_regions_at)
311
312                 .addFunction ("get_nudge_distance", &PublicEditor::get_nudge_distance)
313                 .addFunction ("get_paste_offset", &PublicEditor::get_paste_offset)
314                 .addFunction ("get_grid_beat_divisions", &PublicEditor::get_grid_beat_divisions)
315                 .addFunction ("get_grid_type_as_beats", &PublicEditor::get_grid_type_as_beats)
316
317                 .addFunction ("toggle_ruler_video", &PublicEditor::toggle_ruler_video)
318                 .addFunction ("toggle_xjadeo_proc", &PublicEditor::toggle_xjadeo_proc)
319                 .addFunction ("get_videotl_bar_height", &PublicEditor::get_videotl_bar_height)
320                 .addFunction ("set_video_timeline_height", &PublicEditor::set_video_timeline_height)
321
322 #if 0
323                 .addFunction ("get_route_view_by_route_id", &PublicEditor::get_route_view_by_route_id)
324                 .addFunction ("get_equivalent_regions", &PublicEditor::get_equivalent_regions)
325
326                 .addFunction ("axis_view_from_route", &PublicEditor::axis_view_from_route)
327                 .addFunction ("axis_views_from_routes", &PublicEditor::axis_views_from_routes)
328                 .addFunction ("get_track_views", &PublicEditor::get_track_views)
329                 .addFunction ("drags", &PublicEditor::drags)
330 #endif
331
332                 .addFunction ("center_screen", &PublicEditor::center_screen)
333
334                 .addFunction ("get_smart_mode", &PublicEditor::get_smart_mode)
335                 .addFunction ("get_pointer_position", &PublicEditor::get_pointer_position)
336
337                 .addFunction ("find_location_from_marker", &PublicEditor::find_location_from_marker)
338                 .addFunction ("find_marker_from_location_id", &PublicEditor::find_marker_from_location_id)
339                 .addFunction ("mouse_add_new_marker", &PublicEditor::mouse_add_new_marker)
340 #if 0
341                 .addFunction ("get_regions_at", &PublicEditor::get_regions_at)
342                 .addFunction ("get_regions_after", &PublicEditor::get_regions_after)
343                 .addFunction ("get_regions_from_selection_and_mouse", &PublicEditor::get_regions_from_selection_and_mouse)
344                 .addFunction ("get_regionviews_by_id", &PublicEditor::get_regionviews_by_id)
345                 .addFunction ("get_per_region_note_selection", &PublicEditor::get_per_region_note_selection)
346 #endif
347
348 #if 0
349                 .addFunction ("mouse_add_new_tempo_event", &PublicEditor::mouse_add_new_tempo_event)
350                 .addFunction ("mouse_add_new_meter_event", &PublicEditor::mouse_add_new_meter_event)
351                 .addFunction ("edit_tempo_section", &PublicEditor::edit_tempo_section)
352                 .addFunction ("edit_meter_section", &PublicEditor::edit_meter_section)
353 #endif
354
355                 .addFunction ("access_action", &PublicEditor::access_action)
356                 .endClass ()
357                 .endNamespace ();
358
359 #undef ZOOMFOCUS
360 #undef SNAPTYPE
361 #undef SNAPMODE
362 #undef MOUSEMODE
363 #undef DISPLAYCONTROL
364 #undef IMPORTMODE
365 #undef IMPORTPOSITION
366 #undef IMPORTDISPOSITION
367
368 #define ZOOMFOCUS(NAME) .addConst (stringify(NAME), (Editing::ZoomFocus)Editing::NAME)
369 #define SNAPTYPE(NAME) .addConst (stringify(NAME), (Editing::SnapType)Editing::NAME)
370 #define SNAPMODE(NAME) .addConst (stringify(NAME), (Editing::SnapMode)Editing::NAME)
371 #define MOUSEMODE(NAME) .addConst (stringify(NAME), (Editing::MouseMode)Editing::NAME)
372 #define DISPLAYCONTROL(NAME) .addConst (stringify(NAME), (Editing::DisplayControl)Editing::NAME)
373 #define IMPORTMODE(NAME) .addConst (stringify(NAME), (Editing::ImportMode)Editing::NAME)
374 #define IMPORTPOSITION(NAME) .addConst (stringify(NAME), (Editing::ImportPosition)Editing::NAME)
375 #define IMPORTDISPOSITION(NAME) .addConst (stringify(NAME), (Editing::ImportDisposition)Editing::NAME)
376         luabridge::getGlobalNamespace (L)
377                 .beginNamespace ("Editing")
378 #               include "editing_syms.h"
379                 .endNamespace ();
380 }
381
382 #undef xstr
383 #undef stringify
384
385 ////////////////////////////////////////////////////////////////////////////////
386
387 using namespace ARDOUR;
388 using namespace ARDOUR_UI_UTILS;
389 using namespace PBD;
390 using namespace std;
391
392 #ifndef NDEBUG
393 static void _lua_print (std::string s) {
394         std::cout << "LuaInstance: " << s << "\n";
395 }
396 #endif
397
398 LuaInstance* LuaInstance::_instance = 0;
399
400 LuaInstance*
401 LuaInstance::instance ()
402 {
403         if (!_instance) {
404                 _instance  = new LuaInstance;
405         }
406
407         return _instance;
408 }
409
410 LuaInstance::LuaInstance ()
411 {
412 #ifndef NDEBUG
413         lua.Print.connect (&_lua_print);
414 #endif
415         init ();
416
417         LuaScriptParamList args;
418 }
419
420 LuaInstance::~LuaInstance ()
421 {
422         delete _lua_call_action;
423         delete _lua_add_action;
424         delete _lua_del_action;
425         delete _lua_get_action;
426
427         delete _lua_load;
428         delete _lua_save;
429         delete _lua_clear;
430         _callbacks.clear();
431 }
432
433 void
434 LuaInstance::init ()
435 {
436         lua.do_command (
437                         "function ScriptManager ()"
438                         "  local self = { scripts = {}, instances = {} }"
439                         ""
440                         "  local remove = function (id)"
441                         "   self.scripts[id] = nil"
442                         "   self.instances[id] = nil"
443                         "  end"
444                         ""
445                         "  local addinternal = function (i, n, s, f, a)"
446                         "   assert(type(i) == 'number', 'id must be numeric')"
447                         "   assert(type(n) == 'string', 'Name must be string')"
448                         "   assert(type(s) == 'string', 'Script must be string')"
449                         "   assert(type(f) == 'function', 'Factory is a not a function')"
450                         "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
451                         "   self.scripts[i] = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a }"
452                         "   local env = _ENV;  env.f = nil env.debug = nil os.exit = nil require = nil dofile = nil loadfile = nil package = nil"
453                         "   self.instances[i] = load (string.dump(f, true), nil, nil, env)(a)"
454                         "  end"
455                         ""
456                         "  local call = function (id)"
457                         "   if type(self.instances[id]) == 'function' then"
458                         "     local status, err = pcall (self.instances[id])"
459                         "     if not status then"
460                         "       print ('action \"'.. id .. '\": ', err)" // error out
461                         "       remove (id)"
462                         "     end"
463                         "   end"
464                         "   collectgarbage()"
465                         "  end"
466                         ""
467                         "  local add = function (i, n, s, b, a)"
468                         "   assert(type(b) == 'string', 'ByteCode must be string')"
469                         "   load (b)()" // assigns f
470                         "   assert(type(f) == 'string', 'Assigned ByteCode must be string')"
471                         "   addinternal (i, n, s, load(f), a)"
472                         "  end"
473                         ""
474                         "  local get = function (id)"
475                         "   if type(self.scripts[id]) == 'table' then"
476                         "    return { ['name'] = self.scripts[id]['n'],"
477                         "             ['script'] = self.scripts[id]['s'],"
478                         "             ['args'] = self.scripts[id]['a'] }"
479                         "   end"
480                         "   return nil"
481                         "  end"
482                         ""
483                         "  local function basic_serialize (o)"
484                         "    if type(o) == \"number\" then"
485                         "     return tostring(o)"
486                         "    else"
487                         "     return string.format(\"%q\", o)"
488                         "    end"
489                         "  end"
490                         ""
491                         "  local function serialize (name, value)"
492                         "   local rv = name .. ' = '"
493                         "   collectgarbage()"
494                         "   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
495                         "    return rv .. basic_serialize(value) .. ' '"
496                         "   elseif type(value) == \"table\" then"
497                         "    rv = rv .. '{} '"
498                         "    for k,v in pairs(value) do"
499                         "     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
500                         "     rv = rv .. serialize(fieldname, v) .. ' '"
501                         "     collectgarbage()" // string concatenation allocates a new string
502                         "    end"
503                         "    return rv;"
504                         "   elseif type(value) == \"function\" then"
505                         "     return rv .. string.format(\"%q\", string.dump(value, true))"
506                         "   else"
507                         "    error('cannot save a ' .. type(value))"
508                         "   end"
509                         "  end"
510                         ""
511                         ""
512                         "  local save = function ()"
513                         "   return (serialize('scripts', self.scripts))"
514                         "  end"
515                         ""
516                         "  local clear = function ()"
517                         "   self.scripts = {}"
518                         "   self.instances = {}"
519                         "   collectgarbage()"
520                         "  end"
521                         ""
522                         "  local restore = function (state)"
523                         "   clear()"
524                         "   load (state)()"
525                         "   for i, s in pairs (scripts) do"
526                         "    addinternal (i, s['n'], s['s'], load(s['f']), s['a'])"
527                         "   end"
528                         "   collectgarbage()"
529                         "  end"
530                         ""
531                         " return { call = call, add = add, remove = remove, get = get,"
532                         "          restore = restore, save = save, clear = clear}"
533                         " end"
534                         " "
535                         " manager = ScriptManager ()"
536                         " ScriptManager = nil"
537                         );
538
539         lua_State* L = lua.getState();
540
541         try {
542                 luabridge::LuaRef lua_mgr = luabridge::getGlobal (L, "manager");
543                 lua.do_command ("manager = nil"); // hide it.
544                 lua.do_command ("collectgarbage()");
545
546                 _lua_add_action = new luabridge::LuaRef(lua_mgr["add"]);
547                 _lua_del_action = new luabridge::LuaRef(lua_mgr["remove"]);
548                 _lua_get_action = new luabridge::LuaRef(lua_mgr["get"]);
549                 _lua_call_action = new luabridge::LuaRef(lua_mgr["call"]);
550                 _lua_save = new luabridge::LuaRef(lua_mgr["save"]);
551                 _lua_load = new luabridge::LuaRef(lua_mgr["restore"]);
552                 _lua_clear = new luabridge::LuaRef(lua_mgr["clear"]);
553
554         } catch (luabridge::LuaException const& e) {
555                 fatal << string_compose (_("programming error: %1"),
556                                 X_("Failed to setup Lua action interpreter"))
557                         << endmsg;
558                 abort(); /*NOTREACHED*/
559         }
560
561         register_classes (L);
562
563         luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
564         lua_setglobal (L, "Editor");
565 }
566
567 void LuaInstance::set_session (Session* s)
568 {
569         SessionHandlePtr::set_session (s);
570         if (!_session) {
571                 return;
572         }
573
574         lua_State* L = lua.getState();
575         LuaBindings::set_session (L, _session);
576
577         for (LuaCallbackMap::iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
578                 i->second->set_session (s);
579         }
580 }
581
582 void
583 LuaInstance::session_going_away ()
584 {
585         ENSURE_GUI_THREAD (*this, &LuaInstance::session_going_away);
586         (*_lua_clear)();
587         for (int i = 0; i < 9; ++i) {
588                 ActionChanged (i, ""); /* EMIT SIGNAL */
589         }
590         SessionHandlePtr::session_going_away ();
591         _session = 0;
592
593         lua_State* L = lua.getState();
594         LuaBindings::set_session (L, _session);
595         lua.do_command ("collectgarbage();");
596 }
597
598 int
599 LuaInstance::set_state (const XMLNode& node)
600 {
601         LocaleGuard lg (X_("C"));
602         XMLNode* child;
603
604         if ((child = find_named_node (node, "ActionScript"))) {
605                 for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
606                         if (!(*n)->is_content ()) { continue; }
607                         gsize size;
608                         guchar* buf = g_base64_decode ((*n)->content ().c_str (), &size);
609                         try {
610                                 (*_lua_load)(std::string ((const char*)buf, size));
611                         } catch (luabridge::LuaException const& e) {
612                                 cerr << "LuaException:" << e.what () << endl;
613                         }
614                         for (int i = 0; i < 9; ++i) {
615                                 std::string name;
616                                 if (lua_action_name (i, name)) {
617                                         ActionChanged (i, name); /* EMIT SIGNAL */
618                                 }
619                         }
620                         g_free (buf);
621                 }
622         }
623
624         if ((child = find_named_node (node, "ActionHooks"))) {
625                 for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
626                         try {
627                                 LuaCallbackPtr p (new LuaCallback (_session, *(*n)));
628                                 _callbacks.insert (std::make_pair(p->id(), p));
629                                 p->drop_callback.connect (_slotcon, MISSING_INVALIDATOR, boost::bind (&LuaInstance::unregister_lua_slot, this, p->id()), gui_context());
630                                 SlotChanged (p->id(), p->name(), p->signals()); /* EMIT SIGNAL */
631                         } catch (luabridge::LuaException const& e) {
632                                 cerr << "LuaException:" << e.what () << endl;
633                         }
634                 }
635         }
636
637         return 0;
638 }
639
640 bool
641 LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
642 {
643         std::string title;
644         std::vector<std::string> reg;
645
646         switch (type) {
647                 case LuaScriptInfo::EditorAction:
648                         reg = lua_action_names ();
649                         title = "Add Lua Action";
650                         break;
651                 case LuaScriptInfo::EditorHook:
652                         reg = lua_slot_names ();
653                         title = "Add Lua Callback Hook";
654                         break;
655                 default:
656                         return false;
657         }
658
659         LuaScriptInfoPtr spi;
660         ScriptSelector ss (title, type);
661         switch (ss.run ()) {
662                 case Gtk::RESPONSE_ACCEPT:
663                         spi = ss.script();
664                         break;
665                 default:
666                         return false;
667         }
668
669         std::string script = "";
670
671         try {
672                 script = Glib::file_get_contents (spi->path);
673         } catch (Glib::FileError e) {
674                 string msg = string_compose (_("Cannot read script '%1': %2"), spi->path, e.what());
675                 Gtk::MessageDialog am (msg);
676                 am.run ();
677                 return false;
678         }
679
680         LuaScriptParamList lsp = LuaScripting::script_params (spi, "action_params");
681
682         ScriptParameterDialog spd (_("Set Script Parameters"), spi, reg, lsp);
683         switch (spd.run ()) {
684                 case Gtk::RESPONSE_ACCEPT:
685                         break;
686                 default:
687                         return false;
688         }
689
690         switch (type) {
691                 case LuaScriptInfo::EditorAction:
692                         return set_lua_action (id, spd.name(), script, lsp);
693                         break;
694                 case LuaScriptInfo::EditorHook:
695                         return register_lua_slot (spd.name(), script, lsp);
696                         break;
697                 default:
698                         break;
699         }
700         return false;
701 }
702
703 XMLNode&
704 LuaInstance::get_action_state ()
705 {
706         LocaleGuard lg (X_("C"));
707         std::string saved;
708         {
709                 luabridge::LuaRef savedstate ((*_lua_save)());
710                 saved = savedstate.cast<std::string>();
711         }
712         lua.collect_garbage ();
713
714         gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
715         std::string b64s (b64);
716         g_free (b64);
717
718         XMLNode* script_node = new XMLNode (X_("ActionScript"));
719         script_node->add_property (X_("lua"), LUA_VERSION);
720         script_node->add_content (b64s);
721
722         return *script_node;
723 }
724
725 XMLNode&
726 LuaInstance::get_hook_state ()
727 {
728         XMLNode* script_node = new XMLNode (X_("ActionHooks"));
729         for (LuaCallbackMap::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
730                 script_node->add_child_nocopy (i->second->get_state ());
731         }
732         return *script_node;
733 }
734
735 void
736 LuaInstance::call_action (const int id)
737 {
738         try {
739                 (*_lua_call_action)(id + 1);
740         } catch (luabridge::LuaException const& e) {
741                 cerr << "LuaException:" << e.what () << endl;
742         }
743 }
744
745 bool
746 LuaInstance::set_lua_action (
747                 const int id,
748                 const std::string& name,
749                 const std::string& script,
750                 const LuaScriptParamList& args)
751 {
752         try {
753                 lua_State* L = lua.getState();
754                 // get bytcode of factory-function in a sandbox
755                 // (don't allow scripts to interfere)
756                 const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
757                 luabridge::LuaRef tbl_arg (luabridge::newTable(L));
758                 for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
759                         if ((*i)->optional && !(*i)->is_set) { continue; }
760                         tbl_arg[(*i)->name] = (*i)->value;
761                 }
762                 (*_lua_add_action)(id + 1, name, script, bytecode, tbl_arg);
763                 ActionChanged (id, name); /* EMIT SIGNAL */
764         } catch (luabridge::LuaException const& e) {
765                 cerr << "LuaException:" << e.what () << endl;
766                 return false;
767         }
768         return true;
769 }
770
771 bool
772 LuaInstance::remove_lua_action (const int id)
773 {
774         try {
775                 (*_lua_del_action)(id + 1);
776         } catch (luabridge::LuaException const& e) {
777                 cerr << "LuaException:" << e.what () << endl;
778                 return false;
779         }
780         ActionChanged (id, ""); /* EMIT SIGNAL */
781         return true;
782 }
783
784 bool
785 LuaInstance::lua_action_name (const int id, std::string& rv)
786 {
787         try {
788                 luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
789                 if (ref.isNil()) {
790                         return false;
791                 }
792                 if (ref["name"].isString()) {
793                         rv = ref["name"].cast<std::string>();
794                         return true;
795                 }
796                 return true;
797         } catch (luabridge::LuaException const& e) {
798                 cerr << "LuaException:" << e.what () << endl;
799                 return false;
800         }
801         return false;
802 }
803
804 std::vector<std::string>
805 LuaInstance::lua_action_names ()
806 {
807         std::vector<std::string> rv;
808         for (int i = 0; i < 9; ++i) {
809                 std::string name;
810                 if (lua_action_name (i, name)) {
811                         rv.push_back (name);
812                 }
813         }
814         return rv;
815 }
816
817 bool
818 LuaInstance::lua_action (const int id, std::string& name, std::string& script, LuaScriptParamList& args)
819 {
820         try {
821                 luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
822                 if (ref.isNil()) {
823                         return false;
824                 }
825                 if (!ref["name"].isString()) {
826                         return false;
827                 }
828                 if (!ref["script"].isString()) {
829                         return false;
830                 }
831                 if (!ref["args"].isTable()) {
832                         return false;
833                 }
834                 name = ref["name"].cast<std::string>();
835                 script = ref["script"].cast<std::string>();
836
837                 args.clear();
838                 LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
839                 if (!lsi) {
840                         return false;
841                 }
842                 args = LuaScripting::script_params (lsi, "action_params");
843                 for (luabridge::Iterator i (static_cast<luabridge::LuaRef>(ref["args"])); !i.isNil (); ++i) {
844                         if (!i.key ().isString ()) { assert(0); continue; }
845                         std::string name = i.key ().cast<std::string> ();
846                         std::string value = i.value ().cast<std::string> ();
847                         for (LuaScriptParamList::const_iterator ii = args.begin(); ii != args.end(); ++ii) {
848                                 if ((*ii)->name == name) {
849                                         (*ii)->value = value;
850                                         break;
851                                 }
852                         }
853                 }
854                 return true;
855         } catch (luabridge::LuaException const& e) {
856                 cerr << "LuaException:" << e.what () << endl;
857                 return false;
858         }
859         return false;
860 }
861
862 bool
863 LuaInstance::register_lua_slot (const std::string& name, const std::string& script, const ARDOUR::LuaScriptParamList& args)
864 {
865         /* parse script, get ActionHook(s) from script */
866         ActionHook ah;
867         try {
868                 LuaState l;
869 #ifndef NDEBUG
870                 l.Print.connect (&_lua_print);
871 #endif
872                 lua_State* L = l.getState();
873                 register_hooks (L);
874                 l.do_command ("function ardour () end");
875                 l.do_command (script);
876                 luabridge::LuaRef signals = luabridge::getGlobal (L, "signals");
877                 if (signals.isFunction()) {
878                         ah = signals();
879                 }
880         } catch (luabridge::LuaException const& e) {
881                 cerr << "LuaException:" << e.what () << endl;
882         }
883
884         if (ah.none ()) {
885                 cerr << "Script registered no hooks." << endl;
886                 return false;
887         }
888
889         /* register script w/args, get entry-point / ID */
890
891         try {
892                 LuaCallbackPtr p (new LuaCallback (_session, name, script, ah, args));
893                 _callbacks.insert (std::make_pair(p->id(), p));
894                 p->drop_callback.connect (_slotcon, MISSING_INVALIDATOR, boost::bind (&LuaInstance::unregister_lua_slot, this, p->id()), gui_context());
895                 SlotChanged (p->id(), p->name(), p->signals()); /* EMIT SIGNAL */
896                 return true;
897         } catch (luabridge::LuaException const& e) {
898                 cerr << "LuaException:" << e.what () << endl;
899         }
900         return false;
901 }
902
903 bool
904 LuaInstance::unregister_lua_slot (const PBD::ID& id)
905 {
906         LuaCallbackMap::iterator i = _callbacks.find (id);
907         if (i != _callbacks.end()) {
908                 SlotChanged (id, "", ActionHook()); /* EMIT SIGNAL */
909                 _callbacks.erase (i);
910                 return true;
911         }
912         return false;
913 }
914
915 std::vector<PBD::ID>
916 LuaInstance::lua_slots () const
917 {
918         std::vector<PBD::ID> rv;
919         for (LuaCallbackMap::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
920                 rv.push_back (i->first);
921         }
922         return rv;
923 }
924
925 bool
926 LuaInstance::lua_slot_name (const PBD::ID& id, std::string& name) const
927 {
928         LuaCallbackMap::const_iterator i = _callbacks.find (id);
929         if (i != _callbacks.end()) {
930                 name = i->second->name();
931                 return true;
932         }
933         return false;
934 }
935
936 std::vector<std::string>
937 LuaInstance::lua_slot_names () const
938 {
939         std::vector<std::string> rv;
940         std::vector<PBD::ID> ids = lua_slots();
941         for (std::vector<PBD::ID>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
942                 std::string name;
943                 if (lua_slot_name (*i, name)) {
944                         rv.push_back (name);
945                 }
946         }
947         return rv;
948 }
949
950 bool
951 LuaInstance::lua_slot (const PBD::ID& id, std::string& name, std::string& script, ActionHook& ah, ARDOUR::LuaScriptParamList& args)
952 {
953         LuaCallbackMap::const_iterator i = _callbacks.find (id);
954         if (i == _callbacks.end()) {
955                 return false; // error
956         }
957         return i->second->lua_slot (name, script, ah, args);
958 }
959
960 ///////////////////////////////////////////////////////////////////////////////
961
962 LuaCallback::LuaCallback (Session *s,
963                 const std::string& name,
964                 const std::string& script,
965                 const ActionHook& ah,
966                 const ARDOUR::LuaScriptParamList& args)
967         : SessionHandlePtr (s)
968         , _id ("0")
969         , _name (name)
970         , _signals (ah)
971 {
972         // TODO: allow to reference object (e.g region)
973         init ();
974
975         lua_State* L = lua.getState();
976         luabridge::LuaRef tbl_arg (luabridge::newTable(L));
977         for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
978                 if ((*i)->optional && !(*i)->is_set) { continue; }
979                 tbl_arg[(*i)->name] = (*i)->value;
980         }
981
982         try {
983         const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
984         (*_lua_add)(name, script, bytecode, tbl_arg);
985         } catch (luabridge::LuaException const& e) {
986                 cerr << "LuaException:" << e.what () << endl;
987                 throw failed_constructor ();
988         }
989
990         _id.reset ();
991         set_session (s);
992 }
993
994 LuaCallback::LuaCallback (Session *s, XMLNode & node)
995         : SessionHandlePtr (s)
996 {
997         XMLNode* child = NULL;
998         if (node.name() != X_("LuaCallback")
999                         || !node.property ("signals")
1000                         || !node.property ("id")
1001                         || !node.property ("name")) {
1002                 throw failed_constructor ();
1003         }
1004
1005         for (XMLNodeList::const_iterator n = node.children ().begin (); n != node.children ().end (); ++n) {
1006                 if (!(*n)->is_content ()) { continue; }
1007                 child = *n;
1008         }
1009
1010         if (!child) {
1011                 throw failed_constructor ();
1012         }
1013
1014         init ();
1015
1016         _id = PBD::ID (node.property ("id")->value ());
1017         _name = node.property ("name")->value ();
1018         _signals = ActionHook (node.property ("signals")->value ());
1019
1020         gsize size;
1021         guchar* buf = g_base64_decode (child->content ().c_str (), &size);
1022         try {
1023                 (*_lua_load)(std::string ((const char*)buf, size));
1024         } catch (luabridge::LuaException const& e) {
1025                 cerr << "LuaException:" << e.what () << endl;
1026         }
1027         g_free (buf);
1028
1029         set_session (s);
1030 }
1031
1032 LuaCallback::~LuaCallback ()
1033 {
1034         delete _lua_add;
1035         delete _lua_get;
1036         delete _lua_call;
1037         delete _lua_load;
1038         delete _lua_save;
1039 }
1040
1041 XMLNode&
1042 LuaCallback::get_state (void)
1043 {
1044         std::string saved;
1045         {
1046                 luabridge::LuaRef savedstate ((*_lua_save)());
1047                 saved = savedstate.cast<std::string>();
1048         }
1049         lua.collect_garbage ();
1050
1051         gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
1052         std::string b64s (b64);
1053         g_free (b64);
1054
1055         XMLNode* script_node = new XMLNode (X_("LuaCallback"));
1056         script_node->add_property (X_("lua"), LUA_VERSION);
1057         script_node->add_property (X_("id"), _id.to_s ());
1058         script_node->add_property (X_("name"), _name);
1059         script_node->add_property (X_("signals"), _signals.to_string ());
1060         script_node->add_content (b64s);
1061         return *script_node;
1062 }
1063
1064 void
1065 LuaCallback::init (void)
1066 {
1067 #ifndef NDEBUG
1068         lua.Print.connect (&_lua_print);
1069 #endif
1070
1071         lua.do_command (
1072                         "function ScriptManager ()"
1073                         "  local self = { script = {}, instance = {} }"
1074                         ""
1075                         "  local addinternal = function (n, s, f, a)"
1076                         "   assert(type(n) == 'string', 'Name must be string')"
1077                         "   assert(type(s) == 'string', 'Script must be string')"
1078                         "   assert(type(f) == 'function', 'Factory is a not a function')"
1079                         "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
1080                         "   self.script = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a }"
1081                         "   local env = _ENV;  env.f = nil env.debug = nil os.exit = nil require = nil dofile = nil loadfile = nil package = nil"
1082                         "   self.instance = load (string.dump(f, true), nil, nil, env)(a)"
1083                         "  end"
1084                         ""
1085                         "  local call = function (...)"
1086                         "   if type(self.instance) == 'function' then"
1087                         "     local status, err = pcall (self.instance, ...)"
1088                         "     if not status then"
1089                         "       print ('callback \"'.. self.script['n'] .. '\": ', err)" // error out
1090                         "       self.script = nil"
1091                         "       self.instance = nil"
1092                         "       return false"
1093                         "     end"
1094                         "   end"
1095                         "   collectgarbage()"
1096                         "   return true"
1097                         "  end"
1098                         ""
1099                         "  local add = function (n, s, b, a)"
1100                         "   assert(type(b) == 'string', 'ByteCode must be string')"
1101                         "   load (b)()" // assigns f
1102                         "   assert(type(f) == 'string', 'Assigned ByteCode must be string')"
1103                         "   addinternal (n, s, load(f), a)"
1104                         "  end"
1105                         ""
1106                         "  local get = function ()"
1107                         "   if type(self.instance) == 'function' and type(self.script['n']) == 'string' then"
1108                         "    return { ['name'] = self.script['n'],"
1109                         "             ['script'] = self.script['s'],"
1110                         "             ['args'] = self.script['a'] }"
1111                         "   end"
1112                         "   return nil"
1113                         "  end"
1114                         ""
1115                         // code dup
1116                         ""
1117                         "  local function basic_serialize (o)"
1118                         "    if type(o) == \"number\" then"
1119                         "     return tostring(o)"
1120                         "    else"
1121                         "     return string.format(\"%q\", o)"
1122                         "    end"
1123                         "  end"
1124                         ""
1125                         "  local function serialize (name, value)"
1126                         "   local rv = name .. ' = '"
1127                         "   collectgarbage()"
1128                         "   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
1129                         "    return rv .. basic_serialize(value) .. ' '"
1130                         "   elseif type(value) == \"table\" then"
1131                         "    rv = rv .. '{} '"
1132                         "    for k,v in pairs(value) do"
1133                         "     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
1134                         "     rv = rv .. serialize(fieldname, v) .. ' '"
1135                         "     collectgarbage()" // string concatenation allocates a new string
1136                         "    end"
1137                         "    return rv;"
1138                         "   elseif type(value) == \"function\" then"
1139                         "     return rv .. string.format(\"%q\", string.dump(value, true))"
1140                         "   else"
1141                         "    error('cannot save a ' .. type(value))"
1142                         "   end"
1143                         "  end"
1144                         ""
1145                         // end code dup
1146                         ""
1147                         "  local save = function ()"
1148                         "   return (serialize('s', self.script))"
1149                         "  end"
1150                         ""
1151                         "  local restore = function (state)"
1152                         "   self.script = {}"
1153                         "   load (state)()"
1154                         "   addinternal (s['n'], s['s'], load(s['f']), s['a'])"
1155                         "  end"
1156                         ""
1157                         " return { call = call, add = add, get = get,"
1158                         "          restore = restore, save = save}"
1159                         " end"
1160                         " "
1161                         " manager = ScriptManager ()"
1162                         " ScriptManager = nil"
1163                         );
1164
1165         lua_State* L = lua.getState();
1166
1167         try {
1168                 luabridge::LuaRef lua_mgr = luabridge::getGlobal (L, "manager");
1169                 lua.do_command ("manager = nil"); // hide it.
1170                 lua.do_command ("collectgarbage()");
1171
1172                 _lua_add = new luabridge::LuaRef(lua_mgr["add"]);
1173                 _lua_get = new luabridge::LuaRef(lua_mgr["get"]);
1174                 _lua_call = new luabridge::LuaRef(lua_mgr["call"]);
1175                 _lua_save = new luabridge::LuaRef(lua_mgr["save"]);
1176                 _lua_load = new luabridge::LuaRef(lua_mgr["restore"]);
1177
1178         } catch (luabridge::LuaException const& e) {
1179                 fatal << string_compose (_("programming error: %1"),
1180                                 X_("Failed to setup Lua callback interpreter"))
1181                         << endmsg;
1182                 abort(); /*NOTREACHED*/
1183         }
1184
1185         LuaInstance::register_classes (L);
1186
1187         luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
1188         lua_setglobal (L, "Editor");
1189 }
1190
1191 bool
1192 LuaCallback::lua_slot (std::string& name, std::string& script, ActionHook& ah, ARDOUR::LuaScriptParamList& args)
1193 {
1194         // TODO consolidate w/ LuaInstance::lua_action()
1195         try {
1196                 luabridge::LuaRef ref = (*_lua_get)();
1197                 if (ref.isNil()) {
1198                         return false;
1199                 }
1200                 if (!ref["name"].isString()) {
1201                         return false;
1202                 }
1203                 if (!ref["script"].isString()) {
1204                         return false;
1205                 }
1206                 if (!ref["args"].isTable()) {
1207                         return false;
1208                 }
1209
1210                 ah = _signals;
1211                 name = ref["name"].cast<std::string> ();
1212                 script = ref["script"].cast<std::string> ();
1213
1214                 args.clear();
1215                 LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
1216                 if (!lsi) {
1217                         return false;
1218                 }
1219                 args = LuaScripting::script_params (lsi, "action_params");
1220                 for (luabridge::Iterator i (static_cast<luabridge::LuaRef>(ref["args"])); !i.isNil (); ++i) {
1221                         if (!i.key ().isString ()) { assert(0); continue; }
1222                         std::string name = i.key ().cast<std::string> ();
1223                         std::string value = i.value ().cast<std::string> ();
1224                         for (LuaScriptParamList::const_iterator ii = args.begin(); ii != args.end(); ++ii) {
1225                                 if ((*ii)->name == name) {
1226                                         (*ii)->value = value;
1227                                         break;
1228                                 }
1229                         }
1230                 }
1231                 return true;
1232         } catch (luabridge::LuaException const& e) {
1233                 cerr << "LuaException:" << e.what () << endl;
1234                 return false;
1235         }
1236         return false;
1237 }
1238
1239 void
1240 LuaCallback::set_session (ARDOUR::Session *s)
1241 {
1242         SessionHandlePtr::set_session (s);
1243
1244         if (_session) {
1245                 lua_State* L = lua.getState();
1246                 LuaBindings::set_session (L, _session);
1247         }
1248
1249         reconnect();
1250 }
1251
1252 void
1253 LuaCallback::session_going_away ()
1254 {
1255         ENSURE_GUI_THREAD (*this, &LuaCallback::session_going_away);
1256         lua.do_command ("collectgarbage();");
1257
1258         SessionHandlePtr::session_going_away ();
1259         _session = 0;
1260
1261         drop_callback (); /* EMIT SIGNAL */
1262 }
1263
1264 void
1265 LuaCallback::reconnect ()
1266 {
1267         _connections.drop_connections ();
1268         if ((*_lua_get) ().isNil ()) {
1269                 drop_callback (); /* EMIT SIGNAL */
1270                 return;
1271         }
1272
1273         // TODO pass object which emits the signal (e.g region)
1274         //
1275         // save/load bound objects will be tricky.
1276         // Best idea so far is to save/lookup the PBD::ID
1277         // (either use boost::any indirection or templates for bindable
1278         // object types or a switch statement..)
1279         //
1280         // _session->route_by_id ()
1281         // _session->track_by_diskstream_id ()
1282         // _session->source_by_id ()
1283         // _session->controllable_by_id ()
1284         // _session->processor_by_id ()
1285         // RegionFactory::region_by_id ()
1286         //
1287         // TODO loop over objects (if any)
1288
1289         reconnect_object ((void*)0);
1290 }
1291
1292 template <class T> void
1293 LuaCallback::reconnect_object (T obj)
1294 {
1295         for (uint32_t i = 0; i < LuaSignal::LAST_SIGNAL; ++i) {
1296                 if (_signals[i]) {
1297 #define ENGINE(n,c,p) else if (i == LuaSignal::n) { connect_ ## p (LuaSignal::n, AudioEngine::instance(), &(AudioEngine::instance()->c)); }
1298 #define SESSION(n,c,p) else if (i == LuaSignal::n) { if (_session) { connect_ ## p (LuaSignal::n, _session, &(_session->c)); } }
1299 #define STATIC(n,c,p) else if (i == LuaSignal::n) { connect_ ## p (LuaSignal::n, obj, c); }
1300                         if (0) {}
1301 #                       include "luasignal_syms.h"
1302                         else {
1303                                 PBD::fatal << string_compose (_("programming error: %1: %2"), "Impossible LuaSignal type", i) << endmsg;
1304                                 abort(); /*NOTREACHED*/
1305                         }
1306 #undef ENGINE
1307 #undef SESSION
1308 #undef STATIC
1309                 }
1310         }
1311 }
1312
1313 template <typename T, typename S> void
1314 LuaCallback::connect_0 (enum LuaSignal::LuaSignal ls, T ref, S *signal) {
1315         signal->connect (
1316                         _connections, invalidator (*this),
1317                         boost::bind (&LuaCallback::proxy_0<T>, this, ls, ref),
1318                         gui_context());
1319 }
1320
1321 template <typename T, typename C1> void
1322 LuaCallback::connect_1 (enum LuaSignal::LuaSignal ls, T ref, PBD::Signal1<void, C1> *signal) {
1323         signal->connect (
1324                         _connections, invalidator (*this),
1325                         boost::bind (&LuaCallback::proxy_1<T, C1>, this, ls, ref, _1),
1326                         gui_context());
1327 }
1328
1329 template <typename T, typename C1, typename C2> void
1330 LuaCallback::connect_2 (enum LuaSignal::LuaSignal ls, T ref, PBD::Signal2<void, C1, C2> *signal) {
1331         signal->connect (
1332                         _connections, invalidator (*this),
1333                         boost::bind (&LuaCallback::proxy_2<T, C1, C2>, this, ls, ref, _1, _2),
1334                         gui_context());
1335 }
1336
1337 template <typename T> void
1338 LuaCallback::proxy_0 (enum LuaSignal::LuaSignal ls, T ref) {
1339         luabridge::LuaRef rv ((*_lua_call)((int)ls, ref));
1340         if (! rv.cast<bool> ()) { drop_callback (); /* EMIT SIGNAL */}
1341 }
1342
1343 template <typename T, typename C1> void
1344 LuaCallback::proxy_1 (enum LuaSignal::LuaSignal ls, T ref, C1 a1) {
1345         luabridge::LuaRef rv ((*_lua_call)((int)ls, ref, a1));
1346         if (! rv.cast<bool> ()) { drop_callback (); /* EMIT SIGNAL */}
1347 }
1348
1349 template <typename T, typename C1, typename C2> void
1350 LuaCallback::proxy_2 (enum LuaSignal::LuaSignal ls, T ref, C1 a1, C2 a2) {
1351         luabridge::LuaRef rv ((*_lua_call)((int)ls, ref, a1, a2));
1352         if (! rv.cast<bool> ()) { drop_callback (); /* EMIT SIGNAL */}
1353 }