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