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