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