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