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