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