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