1 /*
2  * Copyright (C) 2016-2019 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2016-2019 Robin Gareus <robin@gareus.org>
4  * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
5  * Copyright (C) 2017-2018 Ben Loftis <ben@harrisonconsoles.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include <cairomm/context.h>
23 #include <cairomm/surface.h>
24 #include <pango/pangocairo.h>
25 
26 #include "pbd/file_utils.h"
27 #include "pbd/strsplit.h"
28 
29 #include "gtkmm2ext/bindings.h"
30 #include "gtkmm2ext/gui_thread.h"
31 
32 #include "ardour/audioengine.h"
33 #include "ardour/disk_reader.h"
34 #include "ardour/disk_writer.h"
35 #include "ardour/filesystem_paths.h"
36 #include "ardour/plugin_manager.h"
37 #include "ardour/route.h"
38 #include "ardour/session.h"
39 #include "ardour/system_exec.h"
40 
41 #include "LuaBridge/LuaBridge.h"
42 
43 #include "ardour_http.h"
44 #include "ardour_ui.h"
45 #include "public_editor.h"
46 #include "region_selection.h"
47 #include "luadialog.h"
48 #include "luainstance.h"
49 #include "luasignal.h"
50 #include "marker.h"
51 #include "mixer_ui.h"
52 #include "region_view.h"
53 #include "processor_box.h"
54 #include "time_axis_view.h"
55 #include "time_axis_view_item.h"
56 #include "selection.h"
57 #include "script_selector.h"
58 #include "timers.h"
59 #include "ui_config.h"
60 #include "utils_videotl.h"
61 
62 #include "pbd/i18n.h"
63 
64 static const char* ui_scripts_file_name = "ui_scripts";
65 
66 namespace LuaCairo {
67 /** wrap RefPtr< Cairo::ImageSurface >
68  *
69  * Image surfaces provide the ability to render to memory buffers either
70  * allocated by cairo or by the calling code. The supported image formats are
71  * those defined in Cairo::Format.
72  */
73 class ImageSurface {
74 	public:
75 		/**
76 		 * Creates an image surface of the specified format and dimensions. Initially
77 		 * the surface contents are all 0. (Specifically, within each pixel, each
78 		 * color or alpha channel belonging to format will be 0. The contents of bits
79 		 * within a pixel, but not belonging to the given format are undefined).
80 		 *
81 		 * @param format  format of pixels in the surface to create
82 		 * @param width   width of the surface, in pixels
83 		 * @param height  height of the surface, in pixels
84 		 */
ImageSurface(Cairo::Format format,int width,int height)85 		ImageSurface (Cairo::Format format, int width, int height)
86 			: _surface (Cairo::ImageSurface::create (format, width, height))
87 			, _ctx (Cairo::Context::create (_surface))
88 			, ctx (_ctx->cobj ()) {}
89 
~ImageSurface()90 		~ImageSurface () {}
91 
92 		/**
93 		 * Set this surface as source for another context.
94 		 * This allows to draw this surface
95 		 */
set_as_source(Cairo::Context * c,int x,int y)96 		void set_as_source (Cairo::Context* c, int x, int y) {
97 			_surface->flush ();
98 			c->set_source (_surface, x, y);
99 		}
100 
101 		/**
102 		 * Returns a context object to perform operations on the surface
103 		 */
context()104 		Cairo::Context* context () {
105 			return (Cairo::Context *)&ctx;
106 		}
107 
108 		/**
109 		 * Returns the stride of the image surface in bytes (or 0 if surface is not
110 		 * an image surface). The stride is the distance in bytes from the beginning
111 		 * of one row of the image data to the beginning of the next row.
112 		 */
get_stride() const113 		int get_stride () const {
114 			return _surface->get_stride ();
115 		}
116 
117 		/** Gets the width of the ImageSurface in pixels */
get_width() const118 		int get_width () const {
119 			return _surface->get_width ();
120 		}
121 
122 		/** Gets the height of the ImageSurface in pixels */
get_height() const123 		int get_height () const {
124 			return _surface->get_height ();
125 		}
126 
127 		/**
128 		 * Get a pointer to the data of the image surface, for direct
129 		 * inspection or modification.
130 		 *
131 		 * Return value: a pointer to the image data of this surface or NULL
132 		 * if @surface is not an image surface.
133 		 *
134 		 */
get_data()135 		unsigned char* get_data () {
136 			return _surface->get_data ();
137 		}
138 
139 		/** Tells cairo to consider the data buffer dirty.
140 		 *
141 		 * In particular, if you've created an ImageSurface with a data buffer that
142 		 * you've allocated yourself and you draw to that data buffer using means
143 		 * other than cairo, you must call mark_dirty() before doing any additional
144 		 * drawing to that surface with cairo.
145 		 *
146 		 * Note that if you do draw to the Surface outside of cairo, you must call
147 		 * flush() before doing the drawing.
148 		 */
mark_dirty()149 		void mark_dirty () {
150 			_surface->mark_dirty ();
151 		}
152 
153 		/** Marks a rectangular area of the given surface dirty.
154 		 *
155 		 * @param x X coordinate of dirty rectangle
156 		 * @param y Y coordinate of dirty rectangle
157 		 * @param width width of dirty rectangle
158 		 * @param height height of dirty rectangle
159 		 */
mark_dirty(int x,int y,int width,int height)160 		void mark_dirty (int x, int y, int width, int height) {
161 			_surface->mark_dirty (x, y, width, height);
162 		}
163 
164 	private:
165 		Cairo::RefPtr<Cairo::ImageSurface> _surface;
166 		Cairo::RefPtr<Cairo::Context> _ctx;
167 		Cairo::Context ctx;
168 };
169 
170 class PangoLayout {
171 	public:
172 		/** Create a new PangoLayout Text Display
173 		 * @param c CairoContext for the layout
174 		 * @param font_name a font-description e.g. "Mono 8px"
175 		 */
PangoLayout(Cairo::Context * c,std::string font_name)176 		PangoLayout (Cairo::Context* c, std::string font_name) {
177 			::PangoLayout* pl = pango_cairo_create_layout (c->cobj ());
178 			_layout = Glib::wrap (pl);
179 			Pango::FontDescription fd (font_name);
180 			_layout->set_font_description (fd);
181 		}
182 
~PangoLayout()183 		~PangoLayout () {}
184 
185 		/** Gets the text in the layout. The returned text should not
186 		 * be freed or modified.
187 		 *
188 		 * @return The text in the @a layout.
189 		 */
get_text() const190 		std::string get_text () const {
191 			return _layout->get_text ();
192 		}
193 		/** Set the text of the layout.
194 		 * @param text The text for the layout.
195 		 */
set_text(const std::string & text)196 		void set_text (const std::string& text) {
197 			_layout->set_text (text);
198 		}
199 
200 		/** Sets the layout text and attribute list from marked-up text (see markup format).
201 		 * Replaces the current text and attribute list.
202 		 * @param markup Some marked-up text.
203 		 */
set_markup(const std::string & markup)204 		void set_markup (const std::string& markup) {
205 			_layout->set_markup (markup);
206 		}
207 
208 		/** Sets the width to which the lines of the Pango::Layout should wrap or
209 		 * ellipsized.  The default value is -1: no width set.
210 		 *
211 		 * @param width The desired width in Pango units, or -1 to indicate that no
212 		 * wrapping or ellipsization should be performed.
213 		 */
set_width(float width)214 		void set_width (float width) {
215 			_layout->set_width (width * PANGO_SCALE);
216 		}
217 
218 		/** Gets the width to which the lines of the Pango::Layout should wrap.
219 		 *
220 		 * @return The width in Pango units, or -1 if no width set.
221 		 */
get_width() const222 		int get_width () const {
223 			return _layout->get_width () / PANGO_SCALE;
224 		}
225 
226 		/** Sets the type of ellipsization being performed for @a layout.
227 		 * Depending on the ellipsization mode @a ellipsize text is
228 		 * removed from the start, middle, or end of text so they
229 		 * fit within the width and height of layout set with
230 		 * set_width() and set_height().
231 		 *
232 		 * If the layout contains characters such as newlines that
233 		 * force it to be layed out in multiple paragraphs, then whether
234 		 * each paragraph is ellipsized separately or the entire layout
235 		 * is ellipsized as a whole depends on the set height of the layout.
236 		 * See set_height() for details.
237 		 *
238 		 * @param ellipsize The new ellipsization mode for @a layout.
239 		 */
set_ellipsize(Pango::EllipsizeMode ellipsize)240 		void set_ellipsize (Pango::EllipsizeMode ellipsize) {
241 			_layout->set_ellipsize (ellipsize);
242 		}
243 
244 		/** Gets the type of ellipsization being performed for @a layout.
245 		 * See set_ellipsize()
246 		 *
247 		 * @return The current ellipsization mode for @a layout.
248 		 *
249 		 * Use is_ellipsized() to query whether any paragraphs
250 		 * were actually ellipsized.
251 		 */
get_ellipsize() const252 		Pango::EllipsizeMode get_ellipsize () const {
253 			return _layout->get_ellipsize ();
254 		}
255 
256 		/** Queries whether the layout had to ellipsize any paragraphs.
257 		 *
258 		 * This returns <tt>true</tt> if the ellipsization mode for @a layout
259 		 * is not Pango::ELLIPSIZE_NONE, a positive width is set on @a layout,
260 		 * and there are paragraphs exceeding that width that have to be
261 		 * ellipsized.
262 		 *
263 		 * @return <tt>true</tt> if any paragraphs had to be ellipsized, <tt>false</tt>
264 		 * otherwise.
265 		 */
is_ellipsized() const266 		bool is_ellipsized () const {
267 			return _layout->is_ellipsized ();
268 		}
269 
270 		/** Sets the alignment for the layout: how partial lines are
271 		 * positioned within the horizontal space available.
272 		 * @param alignment The alignment.
273 		 */
set_alignment(Pango::Alignment alignment)274 		void set_alignment(Pango::Alignment alignment) {
275 			_layout->set_alignment (alignment);
276 		}
277 
278 		/** Gets the alignment for the layout: how partial lines are
279 		 * positioned within the horizontal space available.
280 		 * @return The alignment.
281 		 */
get_alignment() const282 		Pango::Alignment get_alignment() const {
283 			return _layout->get_alignment ();
284 		}
285 
286 		/** Sets the wrap mode; the wrap mode only has effect if a width
287 		 * is set on the layout with set_width().
288 		 * To turn off wrapping, set the width to -1.
289 		 *
290 		 * @param wrap The wrap mode.
291 		 */
set_wrap(Pango::WrapMode wrap)292 		void set_wrap (Pango::WrapMode wrap) {
293 			_layout->set_width (wrap);
294 		}
295 
296 		/** Gets the wrap mode for the layout.
297 		 *
298 		 * Use is_wrapped() to query whether any paragraphs
299 		 * were actually wrapped.
300 		 *
301 		 * @return Active wrap mode.
302 		 */
get_wrap() const303 		Pango::WrapMode get_wrap () const {
304 			return _layout->get_wrap ();
305 		}
306 
307 		/** Queries whether the layout had to wrap any paragraphs.
308 		 *
309 		 * This returns <tt>true</tt> if a positive width is set on @a layout,
310 		 * ellipsization mode of @a layout is set to Pango::ELLIPSIZE_NONE,
311 		 * and there are paragraphs exceeding the layout width that have
312 		 * to be wrapped.
313 		 *
314 		 * @return <tt>true</tt> if any paragraphs had to be wrapped, <tt>false</tt>
315 		 * otherwise.
316 		 */
is_wrapped() const317 		bool is_wrapped () const {
318 			return _layout->is_wrapped ();
319 		}
320 
321 		/** Determines the logical width and height of a Pango::Layout
322 		 * in device units.
323 		 */
get_pixel_size(lua_State * L)324 		int get_pixel_size (lua_State *L) {
325 			int width, height;
326 			_layout->get_pixel_size (width, height);
327 			luabridge::Stack<int>::push (L, width);
328 			luabridge::Stack<int>::push (L, height);
329 			return 2;
330 		}
331 
332 		/** Draws a Layout in the specified Cairo @a context. The top-left
333 		 *  corner of the Layout will be drawn at the current point of the
334 		 *  cairo context.
335 		 *
336 		 * @param context A Cairo context.
337 		 */
show_in_cairo_context(Cairo::Context * c)338 		void show_in_cairo_context (Cairo::Context* c) {
339 			pango_cairo_update_layout (c->cobj (), _layout->gobj());
340 			pango_cairo_show_layout (c->cobj (), _layout->gobj());
341 		}
342 
layout_cairo_path(Cairo::Context * c)343 		void layout_cairo_path (Cairo::Context* c) {
344 			pango_cairo_update_layout (c->cobj (), _layout->gobj());
345 			pango_cairo_layout_path (c->cobj (), _layout->gobj());
346 		}
347 
348 	private:
349 		Glib::RefPtr<Pango::Layout> _layout;
350 };
351 
352 }; // namespace
353 
354 ////////////////////////////////////////////////////////////////////////////////
355 
356 namespace LuaSignal {
357 
358 #define STATIC(name,c,p) else if (!strcmp(type, #name)) {return name;}
359 #define SESSION(name,c,p) else if (!strcmp(type, #name)) {return name;}
360 #define ENGINE(name,c,p) else if (!strcmp(type, #name)) {return name;}
361 
362 LuaSignal
str2luasignal(const std::string & str)363 str2luasignal (const std::string &str) {
364 	const char* type = str.c_str();
365 	if (0) { }
366 #	include "luasignal_syms.h"
367 	else {
368 		PBD::fatal << string_compose (_("programming error: %1: %2"), "Impossible LuaSignal type", str) << endmsg;
369 		abort(); /*NOTREACHED*/
370 	}
371 }
372 #undef STATIC
373 #undef SESSION
374 #undef ENGINE
375 
376 #define STATIC(name,c,p) N_(#name),
377 #define SESSION(name,c,p) N_(#name),
378 #define ENGINE(name,c,p) N_(#name),
379 const char *luasignalstr[] = {
380 #	include "luasignal_syms.h"
381 	0
382 };
383 
384 #undef STATIC
385 #undef SESSION
386 #undef ENGINE
387 }; // namespace
388 
389 
http_get_unlogged(const std::string & url)390 static std::string http_get_unlogged (const std::string& url) { return ArdourCurl::http_get (url, false); }
391 
392 /** special cases for Ardour's Mixer UI */
393 namespace LuaMixer {
394 
395 	ProcessorBox::ProcSelection
processor_selection()396 	processor_selection () {
397 		return ProcessorBox::current_processor_selection ();
398 	}
399 
400 };
401 
mixer_screenshot(const std::string & fn)402 static void mixer_screenshot (const std::string& fn) {
403 	Mixer_UI::instance()->screenshot (fn);
404 }
405 
406 /** Access libardour global configuration */
_ui_config()407 static UIConfiguration* _ui_config () {
408 	return &UIConfiguration::instance();
409 }
410 
411 
412 ////////////////////////////////////////////////////////////////////////////////
413 
414 static PBD::ScopedConnectionList _luaexecs;
415 
reaper(ARDOUR::SystemExec * x)416 static void reaper (ARDOUR::SystemExec* x)
417 {
418 	delete x;
419 }
420 
421 static int
lua_forkexec(lua_State * L)422 lua_forkexec (lua_State *L)
423 {
424 	int argc = lua_gettop (L);
425 	if (argc == 0) {
426 		return luaL_argerror (L, 1, "invalid number of arguments, forkexec (command, ...)");
427 	}
428 	// args are free()ed in ~SystemExec
429 	char** args = (char**) malloc ((argc + 1) * sizeof(char*));
430 	for (int i = 0; i < argc; ++i) {
431 		args[i] = strdup (luaL_checkstring (L, i + 1));
432 	}
433 	args[argc] = 0;
434 
435 	ARDOUR::SystemExec* x = new ARDOUR::SystemExec (args[0], args);
436 	x->Terminated.connect (_luaexecs, MISSING_INVALIDATOR, boost::bind (&reaper, x), gui_context());
437 
438 	if (x->start()) {
439 		reaper (x);
440 		luabridge::Stack<bool>::push (L, false);
441 		return -1;
442 	} else {
443 		luabridge::Stack<bool>::push (L, false);
444 	}
445 	return 1;
446 }
447 
448 #ifndef PLATFORM_WINDOWS
449 static int
lua_exec(std::string cmd)450 lua_exec (std::string cmd)
451 {
452 	// args are free()ed in ~SystemExec
453 	char** args = (char**) malloc (4 * sizeof(char*));
454 	args[0] = strdup ("/bin/sh");
455 	args[1] = strdup ("-c");
456 	args[2] = strdup (cmd.c_str());
457 	args[3] = 0;
458 	ARDOUR::SystemExec x ("/bin/sh", args);
459 	if (x.start()) {
460 		return -1;
461 	}
462 	x.wait ();
463 	return 0;
464 }
465 #endif
466 
467 ////////////////////////////////////////////////////////////////////////////////
468 
469 static int
lua_actionlist(lua_State * L)470 lua_actionlist (lua_State *L)
471 {
472 	using namespace std;
473 
474 	vector<string> paths;
475 	vector<string> labels;
476 	vector<string> tooltips;
477 	vector<string> keys;
478 	vector<Glib::RefPtr<Gtk::Action> > actions;
479 	ActionManager::get_all_actions (paths, labels, tooltips, keys, actions);
480 
481 	vector<string>::iterator p;
482 	vector<string>::iterator l;
483 
484 	luabridge::LuaRef action_tbl (luabridge::newTable (L));
485 
486 	for (l = labels.begin(), p = paths.begin(); l != labels.end(); ++p, ++l) {
487 		if (l->empty ()) {
488 			continue;
489 		}
490 
491 		vector<string> parts;
492 		split (*p, parts, '/');
493 
494 		if (parts.empty()) {
495 			continue;
496 		}
497 
498 		//kinda kludgy way to avoid displaying menu items as mappable
499 		if (parts[1] == _("Main_menu"))
500 			continue;
501 		if (parts[1] == _("JACK"))
502 			continue;
503 		if (parts[1] == _("redirectmenu"))
504 			continue;
505 		if (parts[1] == _("RegionList"))
506 			continue;
507 		if (parts[1] == _("ProcessorMenu"))
508 			continue;
509 
510 		if (!action_tbl[parts[1]].isTable()) {
511 			action_tbl[parts[1]] = luabridge::newTable (L);
512 		}
513 		assert (action_tbl[parts[1]].isTable());
514 		luabridge::LuaRef tbl (action_tbl[parts[1]]);
515 		assert (tbl.isTable());
516 		tbl[*l] = *p;
517 	}
518 
519 	luabridge::push (L, action_tbl);
520 	return 1;
521 }
522 
523 ////////////////////////////////////////////////////////////////////////////////
524 
525 // ARDOUR_UI and instance() are not exposed.
526 ARDOUR::PresentationInfo::order_t
lua_translate_order(RouteDialogs::InsertAt place)527 lua_translate_order (RouteDialogs::InsertAt place)
528 {
529 	return ARDOUR_UI::instance()->translate_order (place);
530 }
531 
532 ////////////////////////////////////////////////////////////////////////////////
533 
534 #define xstr(s) stringify(s)
535 #define stringify(s) #s
536 
537 using namespace ARDOUR;
538 
539 PBD::Signal0<void> LuaInstance::LuaTimerS;
540 PBD::Signal0<void> LuaInstance::LuaTimerDS;
541 PBD::Signal0<void> LuaInstance::SetSession;
542 
543 void
register_hooks(lua_State * L)544 LuaInstance::register_hooks (lua_State* L)
545 {
546 
547 #define ENGINE(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
548 #define STATIC(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
549 #define SESSION(name,c,p) .addConst (stringify(name), (LuaSignal::LuaSignal)LuaSignal::name)
550 	luabridge::getGlobalNamespace (L)
551 		.beginNamespace ("LuaSignal")
552 #		include "luasignal_syms.h"
553 		.endNamespace ();
554 #undef ENGINE
555 #undef SESSION
556 #undef STATIC
557 
558 	luabridge::getGlobalNamespace (L)
559 		.beginNamespace ("LuaSignal")
560 		.beginStdBitSet <LuaSignal::LAST_SIGNAL> ("Set")
561 		.endClass()
562 		.endNamespace ();
563 
564 #if 0 // Dump size -> libs/ardour/luabindings.cc
565 	printf ("LuaInstance: registered %d signals\n", LuaSignal::LAST_SIGNAL);
566 #endif
567 }
568 
569 void
bind_cairo(lua_State * L)570 LuaInstance::bind_cairo (lua_State* L)
571 {
572 	/* std::vector<double> for set_dash()
573 	 * for Windows (DLL, .exe) this needs to be bound in the same memory context as "Cairo".
574 	 *
575 	 * The std::vector<> argument in set_dash() has a fixed address in ardour.exe, while
576 	 * the address of the one in libardour.dll is mapped when loading the .dll
577 	 *
578 	 * see LuaBindings::set_session() for a detailed explanation
579 	 */
580 	luabridge::getGlobalNamespace (L)
581 		.beginNamespace ("C")
582 		.registerArray <double> ("DoubleArray")
583 		.beginStdVector <double> ("DoubleVector")
584 		.endClass ()
585 		.endNamespace ();
586 
587 	luabridge::getGlobalNamespace (L)
588 		.beginNamespace ("Cairo")
589 		.beginClass <Cairo::Context> ("Context")
590 		.addFunction ("save", &Cairo::Context::save)
591 		.addFunction ("restore", &Cairo::Context::restore)
592 		.addFunction ("set_operator", &Cairo::Context::set_operator)
593 		//.addFunction ("set_source", &Cairo::Context::set_operator) // needs RefPtr
594 		.addFunction ("set_source_rgb", &Cairo::Context::set_source_rgb)
595 		.addFunction ("set_source_rgba", &Cairo::Context::set_source_rgba)
596 		.addFunction ("set_line_width", &Cairo::Context::set_line_width)
597 		.addFunction ("set_line_cap", &Cairo::Context::set_line_cap)
598 		.addFunction ("set_line_join", &Cairo::Context::set_line_join)
599 		.addFunction ("set_dash", (void (Cairo::Context::*)(const std::vector<double>&, double))&Cairo::Context::set_dash)
600 		.addFunction ("unset_dash", &Cairo::Context::unset_dash)
601 		.addFunction ("translate", &Cairo::Context::translate)
602 		.addFunction ("scale", &Cairo::Context::scale)
603 		.addFunction ("rotate", &Cairo::Context::rotate)
604 		.addFunction ("begin_new_path", &Cairo::Context::begin_new_path)
605 		.addFunction ("begin_new_sub_path", &Cairo::Context::begin_new_sub_path)
606 		.addFunction ("move_to", &Cairo::Context::move_to)
607 		.addFunction ("line_to", &Cairo::Context::line_to)
608 		.addFunction ("curve_to", &Cairo::Context::curve_to)
609 		.addFunction ("arc", &Cairo::Context::arc)
610 		.addFunction ("arc_negative", &Cairo::Context::arc_negative)
611 		.addFunction ("rel_move_to", &Cairo::Context::rel_move_to)
612 		.addFunction ("rel_line_to", &Cairo::Context::rel_line_to)
613 		.addFunction ("rel_curve_to", &Cairo::Context::rel_curve_to)
614 		.addFunction ("rectangle", (void (Cairo::Context::*)(double, double, double, double))&Cairo::Context::rectangle)
615 		.addFunction ("close_path", &Cairo::Context::close_path)
616 		.addFunction ("paint", &Cairo::Context::paint)
617 		.addFunction ("paint_with_alpha", &Cairo::Context::paint_with_alpha)
618 		.addFunction ("stroke", &Cairo::Context::stroke)
619 		.addFunction ("stroke_preserve", &Cairo::Context::stroke_preserve)
620 		.addFunction ("fill", &Cairo::Context::fill)
621 		.addFunction ("fill_preserve", &Cairo::Context::fill_preserve)
622 		.addFunction ("reset_clip", &Cairo::Context::reset_clip)
623 		.addFunction ("clip", &Cairo::Context::clip)
624 		.addFunction ("clip_preserve", &Cairo::Context::clip_preserve)
625 		.addFunction ("set_font_size", &Cairo::Context::set_font_size)
626 		.addFunction ("show_text", &Cairo::Context::show_text)
627 		.endClass ()
628 		/* enums */
629 		// LineCap, LineJoin, Operator
630 		.beginNamespace ("LineCap")
631 		.addConst ("Butt", CAIRO_LINE_CAP_BUTT)
632 		.addConst ("Round", CAIRO_LINE_CAP_ROUND)
633 		.addConst ("Square", CAIRO_LINE_CAP_SQUARE)
634 		.endNamespace ()
635 
636 		.beginNamespace ("LineJoin")
637 		.addConst ("Miter", CAIRO_LINE_JOIN_MITER)
638 		.addConst ("Round", CAIRO_LINE_JOIN_ROUND)
639 		.addConst ("Bevel", CAIRO_LINE_JOIN_BEVEL)
640 		.endNamespace ()
641 
642 		.beginNamespace ("Operator")
643 		.addConst ("Clear", CAIRO_OPERATOR_CLEAR)
644 		.addConst ("Source", CAIRO_OPERATOR_SOURCE)
645 		.addConst ("Over", CAIRO_OPERATOR_OVER)
646 		.addConst ("Add", CAIRO_OPERATOR_ADD)
647 		.endNamespace ()
648 
649 		.beginNamespace ("Format")
650 		.addConst ("ARGB32", CAIRO_FORMAT_ARGB32)
651 		.addConst ("RGB24", CAIRO_FORMAT_RGB24)
652 		.endNamespace ()
653 
654 		.beginClass <LuaCairo::ImageSurface> ("ImageSurface")
655 		.addConstructor <void (*) (Cairo::Format, int, int)> ()
656 		.addFunction ("set_as_source", &LuaCairo::ImageSurface::set_as_source)
657 		.addFunction ("context", &LuaCairo::ImageSurface::context)
658 		.addFunction ("get_stride", &LuaCairo::ImageSurface::get_stride)
659 		.addFunction ("get_width", &LuaCairo::ImageSurface::get_width)
660 		.addFunction ("get_height", &LuaCairo::ImageSurface::get_height)
661 		//.addFunction ("get_data", &LuaCairo::ImageSurface::get_data) // uint8_t* array is n/a
662 		.endClass ()
663 
664 		.beginClass <LuaCairo::PangoLayout> ("PangoLayout")
665 		.addConstructor <void (*) (Cairo::Context*, std::string)> ()
666 		.addCFunction ("get_pixel_size", &LuaCairo::PangoLayout::get_pixel_size)
667 		.addFunction ("get_text", &LuaCairo::PangoLayout::get_text)
668 		.addFunction ("set_text", &LuaCairo::PangoLayout::set_text)
669 		.addFunction ("show_in_cairo_context", &LuaCairo::PangoLayout::show_in_cairo_context)
670 		.addFunction ("layout_cairo_path", &LuaCairo::PangoLayout::layout_cairo_path)
671 		.addFunction ("set_markup", &LuaCairo::PangoLayout::set_markup)
672 		.addFunction ("set_width", &LuaCairo::PangoLayout::set_width)
673 		.addFunction ("set_ellipsize", &LuaCairo::PangoLayout::set_ellipsize)
674 		.addFunction ("get_ellipsize", &LuaCairo::PangoLayout::get_ellipsize)
675 		.addFunction ("is_ellipsized", &LuaCairo::PangoLayout::is_ellipsized)
676 		.addFunction ("set_alignment", &LuaCairo::PangoLayout::set_alignment)
677 		.addFunction ("get_alignment", &LuaCairo::PangoLayout::get_alignment)
678 		.addFunction ("set_wrap", &LuaCairo::PangoLayout::set_wrap)
679 		.addFunction ("get_wrap", &LuaCairo::PangoLayout::get_wrap)
680 		.addFunction ("is_wrapped", &LuaCairo::PangoLayout::is_wrapped)
681 		.endClass ()
682 
683 		/* enums */
684 		.beginNamespace ("EllipsizeMode")
685 		.addConst ("None", Pango::ELLIPSIZE_NONE)
686 		.addConst ("Start", Pango::ELLIPSIZE_START)
687 		.addConst ("Middle", Pango::ELLIPSIZE_MIDDLE)
688 		.addConst ("End", Pango::ELLIPSIZE_END)
689 		.endNamespace ()
690 
691 		.beginNamespace ("Alignment")
692 		.addConst ("Left", Pango::ALIGN_LEFT)
693 		.addConst ("Center", Pango::ALIGN_CENTER)
694 		.addConst ("Right", Pango::ALIGN_RIGHT)
695 		.endNamespace ()
696 
697 		.beginNamespace ("WrapMode")
698 		.addConst ("Word", Pango::WRAP_WORD)
699 		.addConst ("Char", Pango::WRAP_CHAR)
700 		.addConst ("WordChar", Pango::WRAP_WORD_CHAR)
701 		.endNamespace ()
702 
703 		.endNamespace ();
704 
705 /* Lua/cairo bindings operate on Cairo::Context, there is no Cairo::RefPtr wrapper [yet].
706   one can work around this as follows:
707 
708   LuaState lua;
709   LuaInstance::register_classes (lua.getState());
710   lua.do_command (
711       "function render (ctx)"
712       "  ctx:rectangle (0, 0, 100, 100)"
713       "  ctx:set_source_rgba (0.1, 1.0, 0.1, 1.0)"
714       "  ctx:fill ()"
715       " end"
716       );
717   {
718 		Cairo::RefPtr<Cairo::Context> context = get_window ()->create_cairo_context ();
719     Cairo::Context ctx (context->cobj ());
720 
721     luabridge::LuaRef lua_render = luabridge::getGlobal (lua.getState(), "render");
722     lua_render ((Cairo::Context *)&ctx);
723   }
724 */
725 
726 }
727 
728 void
bind_dialog(lua_State * L)729 LuaInstance::bind_dialog (lua_State* L)
730 {
731 	luabridge::getGlobalNamespace (L)
732 		.beginNamespace ("LuaDialog")
733 
734 		.beginClass <LuaDialog::Message> ("Message")
735 		.addConstructor <void (*) (std::string const&, std::string const&, LuaDialog::Message::MessageType, LuaDialog::Message::ButtonType)> ()
736 		.addFunction ("run", &LuaDialog::Message::run)
737 		.endClass ()
738 
739 		.beginClass <LuaDialog::Dialog> ("Dialog")
740 		.addConstructor <void (*) (std::string const&, luabridge::LuaRef)> ()
741 		.addCFunction ("run", &LuaDialog::Dialog::run)
742 		.endClass ()
743 
744 		/* enums */
745 		.beginNamespace ("MessageType")
746 		.addConst ("Info", LuaDialog::Message::Info)
747 		.addConst ("Warning", LuaDialog::Message::Warning)
748 		.addConst ("Question", LuaDialog::Message::Question)
749 		.addConst ("Error", LuaDialog::Message::Error)
750 		.endNamespace ()
751 
752 		.beginNamespace ("ButtonType")
753 		.addConst ("OK", LuaDialog::Message::OK)
754 		.addConst ("Close", LuaDialog::Message::Close)
755 		.addConst ("Cancel", LuaDialog::Message::Cancel)
756 		.addConst ("Yes_No", LuaDialog::Message::Yes_No)
757 		.addConst ("OK_Cancel", LuaDialog::Message::OK_Cancel)
758 		.endNamespace ()
759 
760 		.beginNamespace ("Response")
761 		.addConst ("OK", 0)
762 		.addConst ("Cancel", 1)
763 		.addConst ("Close", 2)
764 		.addConst ("Yes", 3)
765 		.addConst ("No", 4)
766 		.addConst ("None", -1)
767 		.endNamespace ()
768 
769 		.beginClass <LuaDialog::ProgressWindow> ("ProgressWindow")
770 		.addConstructor <void (*) (std::string const&, bool)> ()
771 		.addFunction ("progress", &LuaDialog::ProgressWindow::progress)
772 		.addFunction ("done", &LuaDialog::ProgressWindow::done)
773 		.addFunction ("canceled", &LuaDialog::ProgressWindow::canceled)
774 		.endClass ()
775 
776 		.endNamespace ();
777 }
778 
779 void
register_classes(lua_State * L)780 LuaInstance::register_classes (lua_State* L)
781 {
782 	LuaBindings::stddef (L);
783 	LuaBindings::common (L);
784 	LuaBindings::session (L);
785 	LuaBindings::osc (L);
786 
787 	bind_cairo (L);
788 	bind_dialog (L);
789 
790 	luabridge::getGlobalNamespace (L)
791 		.beginNamespace ("ArdourUI")
792 
793 		.addFunction ("http_get", &http_get_unlogged)
794 
795 		.addFunction ("mixer_screenshot", &mixer_screenshot)
796 
797 		.addFunction ("processor_selection", &LuaMixer::processor_selection)
798 
799 		.beginStdCPtrList <ArdourMarker> ("ArdourMarkerList")
800 		.endClass ()
801 
802 		.beginClass <ArdourMarker> ("ArdourMarker")
803 		.addFunction ("name", &ArdourMarker::name)
804 		.addFunction ("position", &ArdourMarker::position)
805 		.addFunction ("_type", &ArdourMarker::type)
806 		.endClass ()
807 
808 		.beginClass <AxisView> ("AxisView")
809 		.endClass ()
810 
811 		.deriveClass <TimeAxisView, AxisView> ("TimeAxisView")
812 		.addFunction ("order", &TimeAxisView::order)
813 		.addFunction ("y_position", &TimeAxisView::y_position)
814 		.addFunction ("effective_height", &TimeAxisView::effective_height)
815 		.addFunction ("current_height", &TimeAxisView::current_height)
816 		.addFunction ("set_height", &TimeAxisView::set_height)
817 		.endClass ()
818 
819 		.deriveClass <StripableTimeAxisView, TimeAxisView> ("StripableTimeAxisView")
820 		.endClass ()
821 
822 		.beginClass <Selectable> ("Selectable")
823 		.endClass ()
824 
825 		.deriveClass <TimeAxisViewItem, Selectable> ("TimeAxisViewItem")
826 		.endClass ()
827 
828 		.deriveClass <RegionView, TimeAxisViewItem> ("RegionView")
829 		.endClass ()
830 
831 		.deriveClass <RouteUI, Selectable> ("RouteUI")
832 		.endClass ()
833 
834 		.deriveClass <RouteTimeAxisView, RouteUI> ("RouteTimeAxisView")
835 		.addCast<StripableTimeAxisView> ("to_stripabletimeaxisview")
836 		.addCast<TimeAxisView> ("to_timeaxisview") // deprecated
837 		.endClass ()
838 
839 		// std::list<Selectable*>
840 		.beginStdCPtrList <Selectable> ("SelectionList")
841 		.endClass ()
842 
843 		// std::list<TimeAxisView*>
844 		.beginConstStdCPtrList <TimeAxisView> ("TrackViewStdList")
845 		.endClass ()
846 
847 
848 		.beginClass <RegionSelection> ("RegionSelection")
849 		.addFunction ("start", &RegionSelection::start)
850 		.addFunction ("end_sample", &RegionSelection::end_sample)
851 		.addFunction ("n_midi_regions", &RegionSelection::n_midi_regions)
852 		.addFunction ("regionlist", &RegionSelection::regionlist) // XXX check windows binding (libardour)
853 		.endClass ()
854 
855 		.deriveClass <TimeSelection, std::list<ARDOUR::AudioRange> > ("TimeSelection")
856 		.addFunction ("start", &TimeSelection::start)
857 		.addFunction ("end_sample", &TimeSelection::end_sample)
858 		.addFunction ("length", &TimeSelection::length)
859 		.endClass ()
860 
861 		.deriveClass <MarkerSelection, std::list<ArdourMarker*> > ("MarkerSelection")
862 		.endClass ()
863 
864 		.beginClass <TrackViewList> ("TrackViewList")
865 		.addCast<std::list<TimeAxisView*> > ("to_tav_list")
866 		.addFunction ("contains", &TrackViewList::contains)
867 		.addFunction ("routelist", &TrackViewList::routelist)
868 		.endClass ()
869 
870 		.deriveClass <TrackSelection, TrackViewList> ("TrackSelection")
871 		.endClass ()
872 
873 		.beginClass <Selection> ("Selection")
874 		.addFunction ("clear", &Selection::clear)
875 		.addFunction ("clear_all", &Selection::clear_all)
876 		.addFunction ("empty", &Selection::empty)
877 		.addData ("tracks", &Selection::tracks)
878 		.addData ("regions", &Selection::regions)
879 		.addData ("time", &Selection::time)
880 		.addData ("markers", &Selection::markers)
881 #if 0
882 		.addData ("lines", &Selection::lines)
883 		.addData ("playlists", &Selection::playlists)
884 		.addData ("points", &Selection::points)
885 		.addData ("midi_regions", &Selection::midi_regions)
886 		.addData ("midi_notes", &Selection::midi_notes) // cut buffer only
887 #endif
888 		.endClass ()
889 
890 		.beginClass <PublicEditor> ("Editor")
891 		.addFunction ("grid_type", &PublicEditor::grid_type)
892 		.addFunction ("snap_mode", &PublicEditor::snap_mode)
893 		.addFunction ("set_snap_mode", &PublicEditor::set_snap_mode)
894 
895 		.addFunction ("undo", &PublicEditor::undo)
896 		.addFunction ("redo", &PublicEditor::redo)
897 
898 		.addFunction ("set_mouse_mode", &PublicEditor::set_mouse_mode)
899 		.addFunction ("current_mouse_mode", &PublicEditor::current_mouse_mode)
900 
901 		.addFunction ("consider_auditioning", &PublicEditor::consider_auditioning)
902 
903 		.addFunction ("new_region_from_selection", &PublicEditor::new_region_from_selection)
904 		.addFunction ("separate_region_from_selection", &PublicEditor::separate_region_from_selection)
905 		.addFunction ("pixel_to_sample", &PublicEditor::pixel_to_sample)
906 		.addFunction ("sample_to_pixel", &PublicEditor::sample_to_pixel)
907 
908 		.addFunction ("get_selection", &PublicEditor::get_selection)
909 		.addFunction ("get_cut_buffer", &PublicEditor::get_cut_buffer)
910 		.addRefFunction ("get_selection_extents", &PublicEditor::get_selection_extents)
911 
912 		.addFunction ("set_selection", &PublicEditor::set_selection)
913 
914 		.addFunction ("play_selection", &PublicEditor::play_selection)
915 		.addFunction ("play_with_preroll", &PublicEditor::play_with_preroll)
916 		.addFunction ("maybe_locate_with_edit_preroll", &PublicEditor::maybe_locate_with_edit_preroll)
917 		.addFunction ("goto_nth_marker", &PublicEditor::goto_nth_marker)
918 
919 		.addFunction ("add_location_from_playhead_cursor", &PublicEditor::add_location_from_playhead_cursor)
920 		.addFunction ("remove_location_at_playhead_cursor", &PublicEditor::remove_location_at_playhead_cursor)
921 		.addFunction ("add_location_mark", &PublicEditor::add_location_mark)
922 
923 		.addFunction ("update_grid", &PublicEditor::update_grid)
924 		.addFunction ("remove_tracks", &PublicEditor::remove_tracks)
925 
926 		.addFunction ("set_loop_range", &PublicEditor::set_loop_range)
927 		.addFunction ("set_punch_range", &PublicEditor::set_punch_range)
928 
929 		.addFunction ("effective_mouse_mode", &PublicEditor::effective_mouse_mode)
930 
931 		.addRefFunction ("do_import", &PublicEditor::do_import)
932 		.addRefFunction ("do_embed", &PublicEditor::do_embed)
933 
934 		.addFunction ("export_audio", &PublicEditor::export_audio)
935 		.addFunction ("stem_export", &PublicEditor::stem_export)
936 		.addFunction ("export_selection", &PublicEditor::export_selection)
937 		.addFunction ("export_range", &PublicEditor::export_range)
938 
939 		.addFunction ("set_zoom_focus", &PublicEditor::set_zoom_focus)
940 		.addFunction ("get_zoom_focus", &PublicEditor::get_zoom_focus)
941 		.addFunction ("get_current_zoom", &PublicEditor::get_current_zoom)
942 		.addFunction ("reset_zoom", &PublicEditor::reset_zoom)
943 
944 		.addFunction ("clear_playlist", &PublicEditor::clear_playlist)
945 		.addFunction ("clear_grouped_playlists", &PublicEditor::clear_grouped_playlists)
946 
947 		.addFunction ("new_playlists_for_grouped_tracks", &PublicEditor::new_playlists_for_grouped_tracks)
948 		.addFunction ("new_playlists_for_all_tracks", &PublicEditor::new_playlists_for_all_tracks)
949 		.addFunction ("new_playlists_for_armed_tracks", &PublicEditor::new_playlists_for_armed_tracks)
950 		.addFunction ("new_playlists_for_selected_tracks", &PublicEditor::new_playlists_for_selected_tracks)
951 
952 		.addFunction ("select_all_visible_lanes", &PublicEditor::select_all_visible_lanes)
953 		.addFunction ("select_all_tracks", &PublicEditor::select_all_tracks)
954 		.addFunction ("deselect_all", &PublicEditor::deselect_all)
955 
956 #if 0 // TimeAxisView&  can't be bound (pure virtual fn)
957 		.addFunction ("set_selected_track", &PublicEditor::set_selected_track)
958 		.addFunction ("set_selected_mixer_strip", &PublicEditor::set_selected_mixer_strip)
959 		.addFunction ("ensure_time_axis_view_is_visible", &PublicEditor::ensure_time_axis_view_is_visible)
960 #endif
961 		.addFunction ("hide_track_in_display", &PublicEditor::hide_track_in_display)
962 		.addFunction ("show_track_in_display", &PublicEditor::show_track_in_display)
963 		.addFunction ("set_visible_track_count", &PublicEditor::set_visible_track_count)
964 		.addFunction ("fit_selection", &PublicEditor::fit_selection)
965 
966 		.addFunction ("regionview_from_region", &PublicEditor::regionview_from_region)
967 		.addFunction ("set_stationary_playhead", &PublicEditor::set_stationary_playhead)
968 		.addFunction ("stationary_playhead", &PublicEditor::stationary_playhead)
969 		.addFunction ("set_follow_playhead", &PublicEditor::set_follow_playhead)
970 		.addFunction ("follow_playhead", &PublicEditor::follow_playhead)
971 
972 		.addFunction ("dragging_playhead", &PublicEditor::dragging_playhead)
973 		.addFunction ("leftmost_sample", &PublicEditor::leftmost_sample)
974 		.addFunction ("current_page_samples", &PublicEditor::current_page_samples)
975 		.addFunction ("visible_canvas_height", &PublicEditor::visible_canvas_height)
976 		.addFunction ("temporal_zoom_step", &PublicEditor::temporal_zoom_step)
977 		.addFunction ("override_visible_track_count", &PublicEditor::override_visible_track_count)
978 
979 		.addFunction ("scroll_tracks_down_line", &PublicEditor::scroll_tracks_down_line)
980 		.addFunction ("scroll_tracks_up_line", &PublicEditor::scroll_tracks_up_line)
981 		.addFunction ("scroll_down_one_track", &PublicEditor::scroll_down_one_track)
982 		.addFunction ("scroll_up_one_track", &PublicEditor::scroll_up_one_track)
983 
984 		.addFunction ("reset_x_origin", &PublicEditor::reset_x_origin)
985 		.addFunction ("get_y_origin", &PublicEditor::get_y_origin)
986 		.addFunction ("reset_y_origin", &PublicEditor::reset_y_origin)
987 
988 		.addFunction ("remove_last_capture", &PublicEditor::remove_last_capture)
989 
990 		.addFunction ("maximise_editing_space", &PublicEditor::maximise_editing_space)
991 		.addFunction ("restore_editing_space", &PublicEditor::restore_editing_space)
992 		.addFunction ("toggle_meter_updating", &PublicEditor::toggle_meter_updating)
993 
994 		//.addFunction ("get_preferred_edit_position", &PublicEditor::get_preferred_edit_position)
995 		//.addFunction ("split_regions_at", &PublicEditor::split_regions_at)
996 
997 		.addRefFunction ("get_nudge_distance", &PublicEditor::get_nudge_distance)
998 		.addFunction ("get_paste_offset", &PublicEditor::get_paste_offset)
999 		.addFunction ("get_grid_beat_divisions", &PublicEditor::get_grid_beat_divisions)
1000 		.addRefFunction ("get_grid_type_as_beats", &PublicEditor::get_grid_type_as_beats)
1001 
1002 		.addFunction ("toggle_ruler_video", &PublicEditor::toggle_ruler_video)
1003 		.addFunction ("toggle_xjadeo_proc", &PublicEditor::toggle_xjadeo_proc)
1004 		.addFunction ("get_videotl_bar_height", &PublicEditor::get_videotl_bar_height)
1005 		.addFunction ("set_video_timeline_height", &PublicEditor::set_video_timeline_height)
1006 
1007 #if 0
1008 		.addFunction ("get_equivalent_regions", &PublicEditor::get_equivalent_regions)
1009 		.addFunction ("drags", &PublicEditor::drags)
1010 #endif
1011 
1012 		.addFunction ("get_stripable_time_axis_by_id", &PublicEditor::get_stripable_time_axis_by_id)
1013 		.addFunction ("get_track_views", &PublicEditor::get_track_views)
1014 		.addFunction ("rtav_from_route", &PublicEditor::rtav_from_route)
1015 		.addFunction ("axis_views_from_routes", &PublicEditor::axis_views_from_routes)
1016 
1017 		.addFunction ("center_screen", &PublicEditor::center_screen)
1018 
1019 		.addFunction ("get_smart_mode", &PublicEditor::get_smart_mode)
1020 		.addRefFunction ("get_pointer_position", &PublicEditor::get_pointer_position)
1021 
1022 		.addRefFunction ("find_location_from_marker", &PublicEditor::find_location_from_marker)
1023 		.addFunction ("find_marker_from_location_id", &PublicEditor::find_marker_from_location_id)
1024 		.addFunction ("mouse_add_new_marker", &PublicEditor::mouse_add_new_marker)
1025 #if 0
1026 		.addFunction ("get_regions_at", &PublicEditor::get_regions_at)
1027 		.addFunction ("get_regions_after", &PublicEditor::get_regions_after)
1028 		.addFunction ("get_regions_from_selection_and_mouse", &PublicEditor::get_regions_from_selection_and_mouse)
1029 		.addFunction ("get_regionviews_by_id", &PublicEditor::get_regionviews_by_id)
1030 		.addFunction ("get_per_region_note_selection", &PublicEditor::get_per_region_note_selection)
1031 #endif
1032 
1033 #if 0
1034 		.addFunction ("mouse_add_new_tempo_event", &PublicEditor::mouse_add_new_tempo_event)
1035 		.addFunction ("mouse_add_new_meter_event", &PublicEditor::mouse_add_new_meter_event)
1036 		.addFunction ("edit_tempo_section", &PublicEditor::edit_tempo_section)
1037 		.addFunction ("edit_meter_section", &PublicEditor::edit_meter_section)
1038 #endif
1039 
1040 		.addFunction ("access_action", &PublicEditor::access_action)
1041 		.addFunction ("set_toggleaction", &PublicEditor::set_toggleaction)
1042 		.endClass ()
1043 
1044 		.addFunction ("translate_order", &lua_translate_order)
1045 
1046 		/* ArdourUI enums */
1047 		.beginNamespace ("InsertAt")
1048 		.addConst ("BeforeSelection", RouteDialogs::InsertAt(RouteDialogs::BeforeSelection))
1049 		.addConst ("AfterSelection", RouteDialogs::InsertAt(RouteDialogs::AfterSelection))
1050 		.addConst ("First", RouteDialogs::InsertAt(RouteDialogs::First))
1051 		.addConst ("Last", RouteDialogs::InsertAt(RouteDialogs::Last))
1052 		.endNamespace ()
1053 
1054 		.beginNamespace ("MarkerType")
1055 		.addConst ("Mark", ArdourMarker::Type(ArdourMarker::Mark))
1056 		.addConst ("Tempo", ArdourMarker::Type(ArdourMarker::Tempo))
1057 		.addConst ("Meter", ArdourMarker::Type(ArdourMarker::Meter))
1058 		.addConst ("SessionStart", ArdourMarker::Type(ArdourMarker::SessionStart))
1059 		.addConst ("SessionEnd", ArdourMarker::Type(ArdourMarker::SessionEnd))
1060 		.addConst ("RangeStart", ArdourMarker::Type(ArdourMarker::RangeStart))
1061 		.addConst ("RangeEnd", ArdourMarker::Type(ArdourMarker::RangeEnd))
1062 		.addConst ("LoopStart", ArdourMarker::Type(ArdourMarker::LoopStart))
1063 		.addConst ("LoopEnd", ArdourMarker::Type(ArdourMarker::LoopEnd))
1064 		.addConst ("PunchIn", ArdourMarker::Type(ArdourMarker::PunchIn))
1065 		.addConst ("PunchOut", ArdourMarker::Type(ArdourMarker::PunchOut))
1066 		.endNamespace ()
1067 
1068 		.beginNamespace ("SelectionOp")
1069 		.addConst ("Toggle", Selection::Operation(Selection::Toggle))
1070 		.addConst ("Set", Selection::Operation(Selection::Set))
1071 		.addConst ("Extend", Selection::Operation(Selection::Extend))
1072 		.addConst ("Add", Selection::Operation(Selection::Add))
1073 		.endNamespace ()
1074 
1075 		.beginNamespace ("TrackHeightMode")
1076 		.addConst ("OnlySelf", TimeAxisView::TrackHeightMode(TimeAxisView::OnlySelf))
1077 		.addConst ("TotalHeight", TimeAxisView::TrackHeightMode(TimeAxisView::TotalHeight))
1078 		.addConst ("HeightPerLane,", TimeAxisView::TrackHeightMode(TimeAxisView::HeightPerLane))
1079 		.endNamespace ()
1080 
1081 		.addCFunction ("actionlist", &lua_actionlist)
1082 
1083 
1084 		.beginClass <UIConfiguration> ("UIConfiguration")
1085 #undef  UI_CONFIG_VARIABLE
1086 #define UI_CONFIG_VARIABLE(Type,var,name,value) \
1087 		.addFunction ("get_" # var, &UIConfiguration::get_##var) \
1088 		.addFunction ("set_" # var, &UIConfiguration::set_##var) \
1089 		.addProperty (#var, &UIConfiguration::get_##var, &UIConfiguration::set_##var)
1090 
1091 #include "ui_config_vars.h"
1092 
1093 #undef UI_CONFIG_VARIABLE
1094 		.endClass()
1095 
1096 		.addFunction ("config", &_ui_config)
1097 
1098 		.endNamespace () // end ArdourUI
1099 
1100 		.beginNamespace ("os")
1101 #ifndef PLATFORM_WINDOWS
1102 		.addFunction ("execute", &lua_exec)
1103 #endif
1104 		.addCFunction ("forkexec", &lua_forkexec)
1105 		.endNamespace ();
1106 
1107 	// Editing Symbols
1108 
1109 #undef ZOOMFOCUS
1110 #undef GRIDTYPE
1111 #undef SNAPMODE
1112 #undef MOUSEMODE
1113 #undef DISPLAYCONTROL
1114 #undef IMPORTMODE
1115 #undef IMPORTPOSITION
1116 #undef IMPORTDISPOSITION
1117 
1118 #define ZOOMFOCUS(NAME) .addConst (stringify(NAME), (Editing::ZoomFocus)Editing::NAME)
1119 #define GRIDTYPE(NAME) .addConst (stringify(NAME), (Editing::GridType)Editing::NAME)
1120 #define SNAPMODE(NAME) .addConst (stringify(NAME), (Editing::SnapMode)Editing::NAME)
1121 #define MOUSEMODE(NAME) .addConst (stringify(NAME), (Editing::MouseMode)Editing::NAME)
1122 #define DISPLAYCONTROL(NAME) .addConst (stringify(NAME), (Editing::DisplayControl)Editing::NAME)
1123 #define IMPORTMODE(NAME) .addConst (stringify(NAME), (Editing::ImportMode)Editing::NAME)
1124 #define IMPORTPOSITION(NAME) .addConst (stringify(NAME), (Editing::ImportPosition)Editing::NAME)
1125 #define IMPORTDISPOSITION(NAME) .addConst (stringify(NAME), (Editing::ImportDisposition)Editing::NAME)
1126 	luabridge::getGlobalNamespace (L)
1127 		.beginNamespace ("Editing")
1128 #		include "editing_syms.h"
1129 		.endNamespace ();
1130 }
1131 
1132 #undef xstr
1133 #undef stringify
1134 
1135 ////////////////////////////////////////////////////////////////////////////////
1136 
1137 using namespace ARDOUR;
1138 using namespace ARDOUR_UI_UTILS;
1139 using namespace PBD;
1140 using namespace std;
1141 
_lua_print(std::string s)1142 static void _lua_print (std::string s) {
1143 #ifndef NDEBUG
1144 	std::cout << "LuaInstance: " << s << "\n";
1145 #endif
1146 	PBD::info << "LuaInstance: " << s << endmsg;
1147 }
1148 
1149 LuaInstance* LuaInstance::_instance = 0;
1150 
1151 LuaInstance*
instance()1152 LuaInstance::instance ()
1153 {
1154 	if (!_instance) {
1155 		_instance  = new LuaInstance;
1156 	}
1157 
1158 	return _instance;
1159 }
1160 
1161 void
destroy_instance()1162 LuaInstance::destroy_instance ()
1163 {
1164 	delete _instance;
1165 	_instance = 0;
1166 }
1167 
LuaInstance()1168 LuaInstance::LuaInstance ()
1169 {
1170 	lua.Print.connect (&_lua_print);
1171 	init ();
1172 }
1173 
~LuaInstance()1174 LuaInstance::~LuaInstance ()
1175 {
1176 	delete _lua_call_action;
1177 	delete _lua_render_icon;
1178 	delete _lua_add_action;
1179 	delete _lua_del_action;
1180 	delete _lua_get_action;
1181 
1182 	delete _lua_load;
1183 	delete _lua_save;
1184 	delete _lua_clear;
1185 	_callbacks.clear();
1186 }
1187 
1188 void
init()1189 LuaInstance::init ()
1190 {
1191 	lua.sandbox (false);
1192 	lua.do_command (
1193 			"function ScriptManager ()"
1194 			"  local self = { scripts = {}, instances = {}, icons = {} }"
1195 			""
1196 			"  local remove = function (id)"
1197 			"   self.scripts[id] = nil"
1198 			"   self.instances[id] = nil"
1199 			"   self.icons[id] = nil"
1200 			"  end"
1201 			""
1202 			"  local addinternal = function (i, n, s, f, c, a)"
1203 			"   assert(type(i) == 'number', 'id must be numeric')"
1204 			"   assert(type(n) == 'string', 'Name must be string')"
1205 			"   assert(type(s) == 'string', 'Script must be string')"
1206 			"   assert(type(f) == 'function', 'Factory is a not a function')"
1207 			"   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
1208 			"   self.scripts[i] = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a, ['c'] = c }"
1209 			"   local env = _ENV; env.f = nil"
1210 			"   self.instances[i] = load (string.dump(f, true), nil, nil, env)(a)"
1211 			"   if type(c) == 'function' then"
1212 			"     self.icons[i] = load (string.dump(c, true), nil, nil, env)(a)"
1213 			"   else"
1214 			"     self.icons[i] = nil"
1215 			"   end"
1216 			"  end"
1217 			""
1218 			"  local call = function (id)"
1219 			"   if type(self.instances[id]) == 'function' then"
1220 			"     local status, err = pcall (self.instances[id])"
1221 			"     if not status then"
1222 			"       print ('action \"'.. id .. '\": ', err)" // error out
1223 			"       remove (id)"
1224 			"     end"
1225 			"   end"
1226 			"   collectgarbage()"
1227 			"  end"
1228 			""
1229 			"  local icon = function (id, ...)"
1230 			"   if type(self.icons[id]) == 'function' then"
1231 			"     pcall (self.icons[id], ...)"
1232 			"   end"
1233 			"   collectgarbage()"
1234 			"  end"
1235 			""
1236 			"  local add = function (i, n, s, b, c, a)"
1237 			"   assert(type(b) == 'string', 'ByteCode must be string')"
1238 			"   f = nil load (b)()" // assigns f
1239 			"   icn = nil load (c)()" // may assign "icn"
1240 			"   assert(type(f) == 'string', 'Assigned ByteCode must be string')"
1241 			"   addinternal (i, n, s, load(f), type(icn) ~= \"string\" or icn == '' or load(icn), a)"
1242 			"  end"
1243 			""
1244 			"  local get = function (id)"
1245 			"   if type(self.scripts[id]) == 'table' then"
1246 			"    return { ['name'] = self.scripts[id]['n'],"
1247 			"             ['script'] = self.scripts[id]['s'],"
1248 			"             ['icon'] = type(self.scripts[id]['c']) == 'function',"
1249 			"             ['args'] = self.scripts[id]['a'] }"
1250 			"   end"
1251 			"   return nil"
1252 			"  end"
1253 			""
1254 			"  local function basic_serialize (o)"
1255 			"    if type(o) == \"number\" then"
1256 			"     return tostring(o)"
1257 			"    else"
1258 			"     return string.format(\"%q\", o)"
1259 			"    end"
1260 			"  end"
1261 			""
1262 			"  local function serialize (name, value)"
1263 			"   local rv = name .. ' = '"
1264 			"   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
1265 			"    return rv .. basic_serialize(value) .. ' '"
1266 			"   elseif type(value) == \"table\" then"
1267 			"    rv = rv .. '{} '"
1268 			"    for k,v in pairs(value) do"
1269 			"     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
1270 			"     rv = rv .. serialize(fieldname, v) .. ' '"
1271 			"    end"
1272 			"    return rv;"
1273 			"   elseif type(value) == \"function\" then"
1274 			"     return rv .. string.format(\"%q\", string.dump(value, true))"
1275 			"   elseif type(value) == \"boolean\" then"
1276 			"     return rv .. tostring (value)"
1277 			"   else"
1278 			"    error('cannot save a ' .. type(value))"
1279 			"   end"
1280 			"  end"
1281 			""
1282 			""
1283 			"  local save = function ()"
1284 			"   return (serialize('scripts', self.scripts))"
1285 			"  end"
1286 			""
1287 			"  local clear = function ()"
1288 			"   self.scripts = {}"
1289 			"   self.instances = {}"
1290 			"   self.icons = {}"
1291 			"   collectgarbage()"
1292 			"  end"
1293 			""
1294 			"  local restore = function (state)"
1295 			"   clear()"
1296 			"   load (state)()"
1297 			"   for i, s in pairs (scripts) do"
1298 			"    addinternal (i, s['n'], s['s'], load(s['f']), type (s['c']) ~= \"string\" or s['c'] == '' or load (s['c']), s['a'])"
1299 			"   end"
1300 			"   collectgarbage()"
1301 			"  end"
1302 			""
1303 			" return { call = call, add = add, remove = remove, get = get,"
1304 			"          restore = restore, save = save, clear = clear, icon = icon}"
1305 			" end"
1306 			" "
1307 			" manager = ScriptManager ()"
1308 			" ScriptManager = nil"
1309 			);
1310 	lua_State* L = lua.getState();
1311 
1312 	try {
1313 		luabridge::LuaRef lua_mgr = luabridge::getGlobal (L, "manager");
1314 		lua.do_command ("manager = nil"); // hide it.
1315 		lua.do_command ("collectgarbage()");
1316 
1317 		_lua_add_action = new luabridge::LuaRef(lua_mgr["add"]);
1318 		_lua_del_action = new luabridge::LuaRef(lua_mgr["remove"]);
1319 		_lua_get_action = new luabridge::LuaRef(lua_mgr["get"]);
1320 		_lua_call_action = new luabridge::LuaRef(lua_mgr["call"]);
1321 		_lua_render_icon = new luabridge::LuaRef(lua_mgr["icon"]);
1322 		_lua_save = new luabridge::LuaRef(lua_mgr["save"]);
1323 		_lua_load = new luabridge::LuaRef(lua_mgr["restore"]);
1324 		_lua_clear = new luabridge::LuaRef(lua_mgr["clear"]);
1325 
1326 	} catch (luabridge::LuaException const& e) {
1327 		fatal << string_compose (_("programming error: %1"),
1328 				std::string ("Failed to setup Lua action interpreter") + e.what ())
1329 			<< endmsg;
1330 		abort(); /*NOTREACHED*/
1331 	} catch (...) {
1332 		fatal << string_compose (_("programming error: %1"),
1333 				X_("Failed to setup Lua action interpreter"))
1334 			<< endmsg;
1335 		abort(); /*NOTREACHED*/
1336 	}
1337 
1338 	register_classes (L);
1339 	register_hooks (L);
1340 
1341 	luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
1342 	lua_setglobal (L, "Editor");
1343 }
1344 
1345 int
load_state()1346 LuaInstance::load_state ()
1347 {
1348 	std::string uiscripts;
1349 	if (!find_file (ardour_config_search_path(), ui_scripts_file_name, uiscripts)) {
1350 		pre_seed_scripts ();
1351 		return -1;
1352 	}
1353 	XMLTree tree;
1354 
1355 	info << string_compose (_("Loading user ui scripts file %1"), uiscripts) << endmsg;
1356 
1357 	if (!tree.read (uiscripts)) {
1358 		error << string_compose(_("cannot read ui scripts file \"%1\""), uiscripts) << endmsg;
1359 		return -1;
1360 	}
1361 
1362 	if (set_state (*tree.root())) {
1363 		error << string_compose(_("user ui scripts file \"%1\" not loaded successfully."), uiscripts) << endmsg;
1364 		return -1;
1365 	}
1366 
1367 	return 0;
1368 }
1369 
1370 int
save_state()1371 LuaInstance::save_state ()
1372 {
1373 	if (!_session) {
1374 		/* action scripts are un-registered with the session */
1375 		return -1;
1376 	}
1377 
1378 	std::string uiscripts = Glib::build_filename (user_config_directory(), ui_scripts_file_name);
1379 
1380 	XMLNode* node = new XMLNode (X_("UIScripts"));
1381 	node->add_child_nocopy (get_action_state ());
1382 	node->add_child_nocopy (get_hook_state ());
1383 
1384 	XMLTree tree;
1385 	tree.set_root (node);
1386 
1387 	if (!tree.write (uiscripts.c_str())){
1388 		error << string_compose (_("UI script file %1 not saved"), uiscripts) << endmsg;
1389 		return -1;
1390 	}
1391 	return 0;
1392 }
1393 
1394 void
set_dirty()1395 LuaInstance::set_dirty ()
1396 {
1397 	if (!_session || _session->deletion_in_progress()) {
1398 		return;
1399 	}
1400 	save_state ();
1401 	_session->set_dirty (); // XXX is this reasonable?
1402 }
1403 
set_session(Session * s)1404 void LuaInstance::set_session (Session* s)
1405 {
1406 	SessionHandlePtr::set_session (s);
1407 	if (!_session) {
1408 		return;
1409 	}
1410 
1411 	load_state ();
1412 
1413 	lua_State* L = lua.getState();
1414 	LuaBindings::set_session (L, _session);
1415 
1416 	for (LuaCallbackMap::iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
1417 		i->second->set_session (s);
1418 	}
1419 	second_connection = Timers::rapid_connect (sigc::mem_fun(*this, & LuaInstance::every_second));
1420 	point_one_second_connection = Timers::rapid_connect (sigc::mem_fun(*this, & LuaInstance::every_point_one_seconds));
1421 	SetSession (); /* EMIT SIGNAL */
1422 }
1423 
1424 void
session_going_away()1425 LuaInstance::session_going_away ()
1426 {
1427 	ENSURE_GUI_THREAD (*this, &LuaInstance::session_going_away);
1428 	second_connection.disconnect ();
1429 	point_one_second_connection.disconnect ();
1430 
1431 	(*_lua_clear)();
1432 	for (int i = 0; i < MAX_LUA_ACTION_SCRIPTS; ++i) {
1433 		ActionChanged (i, ""); /* EMIT SIGNAL */
1434 	}
1435 	SessionHandlePtr::session_going_away ();
1436 	_session = 0;
1437 
1438 	lua_State* L = lua.getState();
1439 	LuaBindings::set_session (L, _session);
1440 	lua.collect_garbage ();
1441 }
1442 
1443 void
every_second()1444 LuaInstance::every_second ()
1445 {
1446 	LuaTimerS (); // emit signal
1447 }
1448 
1449 void
every_point_one_seconds()1450 LuaInstance::every_point_one_seconds ()
1451 {
1452 	LuaTimerDS (); // emit signal
1453 }
1454 
1455 int
set_state(const XMLNode & node)1456 LuaInstance::set_state (const XMLNode& node)
1457 {
1458 	XMLNode* child;
1459 
1460 	if ((child = find_named_node (node, "ActionScript"))) {
1461 		for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
1462 			if (!(*n)->is_content ()) { continue; }
1463 			gsize size;
1464 			guchar* buf = g_base64_decode ((*n)->content ().c_str (), &size);
1465 			try {
1466 				(*_lua_load)(std::string ((const char*)buf, size));
1467 			} catch (luabridge::LuaException const& e) {
1468 #ifndef NDEBUG
1469 				cerr << "LuaException:" << e.what () << endl;
1470 #endif
1471 				PBD::warning << "LuaException: " << e.what () << endmsg;
1472 			} catch (...) { }
1473 			for (int i = 0; i < MAX_LUA_ACTION_SCRIPTS; ++i) {
1474 				std::string name;
1475 				if (lua_action_name (i, name)) {
1476 					ActionChanged (i, name); /* EMIT SIGNAL */
1477 				}
1478 			}
1479 			g_free (buf);
1480 		}
1481 	}
1482 
1483 	assert (_callbacks.empty());
1484 	if ((child = find_named_node (node, "ActionHooks"))) {
1485 		for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
1486 			try {
1487 				LuaCallbackPtr p (new LuaCallback (_session, *(*n)));
1488 				_callbacks.insert (std::make_pair(p->id(), p));
1489 				p->drop_callback.connect (_slotcon, MISSING_INVALIDATOR, boost::bind (&LuaInstance::unregister_lua_slot, this, p->id()), gui_context());
1490 				SlotChanged (p->id(), p->name(), p->signals()); /* EMIT SIGNAL */
1491 			} catch (luabridge::LuaException const& e) {
1492 #ifndef NDEBUG
1493 				cerr << "LuaException:" << e.what () << endl;
1494 #endif
1495 				PBD::warning << "LuaException: " << e.what () << endmsg;
1496 			} catch (...) { }
1497 		}
1498 	}
1499 
1500 	return 0;
1501 }
1502 
1503 void
pre_seed_script(std::string const & name,int & id)1504 LuaInstance::pre_seed_script (std::string const& name, int& id)
1505 {
1506 	LuaScriptInfoPtr spi = LuaScripting::instance ().by_name (name, LuaScriptInfo::EditorAction);
1507 	if (spi) {
1508 		try {
1509 			std::string script = Glib::file_get_contents (spi->path);
1510 			LuaState ls;
1511 			register_classes (ls.getState ());
1512 			LuaScriptParamList lsp = LuaScriptParams::script_params (ls, spi->path, "action_params");
1513 			LuaScriptParamPtr lspp (new LuaScriptParam("x-script-origin", "", spi->path, false, true));
1514 			lsp.push_back (lspp);
1515 			set_lua_action (id++, name, script, lsp);
1516 		} catch (...) { }
1517 	}
1518 }
1519 
1520 void
pre_seed_scripts()1521 LuaInstance::pre_seed_scripts ()
1522 {
1523 	int id = 0;
1524 	pre_seed_script ("Mixer Screenshot", id);
1525 	pre_seed_script ("List Plugins", id);
1526 }
1527 
1528 bool
interactive_add(LuaScriptInfo::ScriptType type,int id)1529 LuaInstance::interactive_add (LuaScriptInfo::ScriptType type, int id)
1530 {
1531 	std::string title;
1532 	std::string param_function = "action_params";
1533 	std::vector<std::string> reg;
1534 
1535 	switch (type) {
1536 		case LuaScriptInfo::EditorAction:
1537 			reg = lua_action_names ();
1538 			title = _("Add Shortcut or Lua Script");
1539 			break;
1540 		case LuaScriptInfo::EditorHook:
1541 			reg = lua_slot_names ();
1542 			title = _("Add Lua Callback Hook");
1543 			break;
1544 		case LuaScriptInfo::Session:
1545 			if (!_session) {
1546 				return false;
1547 			}
1548 			reg = _session->registered_lua_functions ();
1549 			title = _("Add Lua Session Script");
1550 			param_function = "sess_params";
1551 			break;
1552 		default:
1553 			return false;
1554 	}
1555 
1556 	LuaScriptInfoPtr spi;
1557 	ScriptSelector ss (title, type);
1558 	switch (ss.run ()) {
1559 		case Gtk::RESPONSE_ACCEPT:
1560 			spi = ss.script();
1561 			break;
1562 		default:
1563 			return false;
1564 	}
1565 	ss.hide ();
1566 
1567 	std::string script = "";
1568 
1569 	try {
1570 		script = Glib::file_get_contents (spi->path);
1571 	} catch (Glib::FileError const& e) {
1572 		string msg = string_compose (_("Cannot read script '%1': %2"), spi->path, e.what());
1573 		Gtk::MessageDialog am (msg);
1574 		am.run ();
1575 		return false;
1576 	}
1577 
1578 	LuaState ls;
1579 	register_classes (ls.getState ());
1580 	LuaScriptParamList lsp = LuaScriptParams::script_params (ls, spi->path, param_function);
1581 
1582 	/* allow cancel */
1583 	for (size_t i = 0; i < lsp.size(); ++i) {
1584 		if (lsp[i]->preseeded && lsp[i]->name == "x-script-abort") {
1585 			return false;
1586 		}
1587 	}
1588 
1589 	ScriptParameterDialog spd (_("Set Script Parameters"), spi, reg, lsp);
1590 
1591 	if (spd.need_interation ()) {
1592 		switch (spd.run ()) {
1593 			case Gtk::RESPONSE_ACCEPT:
1594 				break;
1595 			default:
1596 				return false;
1597 		}
1598 	}
1599 
1600 	LuaScriptParamPtr lspp (new LuaScriptParam("x-script-origin", "", spi->path, false, true));
1601 	lsp.push_back (lspp);
1602 
1603 	switch (type) {
1604 		case LuaScriptInfo::EditorAction:
1605 			return set_lua_action (id, spd.name(), script, lsp);
1606 			break;
1607 		case LuaScriptInfo::EditorHook:
1608 			return register_lua_slot (spd.name(), script, lsp);
1609 			break;
1610 		case LuaScriptInfo::Session:
1611 			try {
1612 				_session->register_lua_function (spd.name(), script, lsp);
1613 			} catch (luabridge::LuaException const& e) {
1614 				string msg = string_compose (_("Session script '%1' instantiation failed: %2"), spd.name(), e.what ());
1615 				Gtk::MessageDialog am (msg);
1616 				am.run ();
1617 			} catch (SessionException const& e) {
1618 				string msg = string_compose (_("Loading Session script '%1' failed: %2"), spd.name(), e.what ());
1619 				Gtk::MessageDialog am (msg);
1620 				am.run ();
1621 			} catch (...) {
1622 				string msg = string_compose (_("Loading Session script '%1' failed: %2"), spd.name(), "Unknown Exception");
1623 				Gtk::MessageDialog am (msg);
1624 				am.run ();
1625 			}
1626 		default:
1627 			break;
1628 	}
1629 	return false;
1630 }
1631 
1632 XMLNode&
get_action_state()1633 LuaInstance::get_action_state ()
1634 {
1635 	std::string saved;
1636 	{
1637 		luabridge::LuaRef savedstate ((*_lua_save)());
1638 		saved = savedstate.cast<std::string>();
1639 	}
1640 	lua.collect_garbage ();
1641 
1642 	gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
1643 	std::string b64s (b64);
1644 	g_free (b64);
1645 
1646 	XMLNode* script_node = new XMLNode (X_("ActionScript"));
1647 	script_node->set_property (X_("lua"), LUA_VERSION);
1648 	script_node->add_content (b64s);
1649 
1650 	return *script_node;
1651 }
1652 
1653 XMLNode&
get_hook_state()1654 LuaInstance::get_hook_state ()
1655 {
1656 	XMLNode* script_node = new XMLNode (X_("ActionHooks"));
1657 	for (LuaCallbackMap::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
1658 		script_node->add_child_nocopy (i->second->get_state ());
1659 	}
1660 	return *script_node;
1661 }
1662 
1663 void
call_action(const int id)1664 LuaInstance::call_action (const int id)
1665 {
1666 	try {
1667 		(*_lua_call_action)(id + 1);
1668 		lua.collect_garbage_step ();
1669 	} catch (luabridge::LuaException const& e) {
1670 #ifndef NDEBUG
1671 		cerr << "LuaException:" << e.what () << endl;
1672 #endif
1673 		PBD::warning << "LuaException: " << e.what () << endmsg;
1674 	} catch (...) { }
1675 }
1676 
1677 void
render_action_icon(cairo_t * cr,int w,int h,uint32_t c,void * i)1678 LuaInstance::render_action_icon (cairo_t* cr, int w, int h, uint32_t c, void* i) {
1679 	int ii = reinterpret_cast<uintptr_t> (i);
1680 	instance()->render_icon (ii, cr, w, h, c);
1681 }
1682 
1683 void
render_icon(int i,cairo_t * cr,int w,int h,uint32_t clr)1684 LuaInstance::render_icon (int i, cairo_t* cr, int w, int h, uint32_t clr)
1685 {
1686 	 Cairo::Context ctx (cr);
1687 	 try {
1688 		 (*_lua_render_icon)(i + 1, (Cairo::Context *)&ctx, w, h, clr);
1689 	 } catch (luabridge::LuaException const& e) {
1690 #ifndef NDEBUG
1691 		 cerr << "LuaException:" << e.what () << endl;
1692 #endif
1693 		 PBD::warning << "LuaException: " << e.what () << endmsg;
1694 	 } catch (...) { }
1695 }
1696 
1697 bool
set_lua_action(const int id,const std::string & name,const std::string & script,const LuaScriptParamList & args)1698 LuaInstance::set_lua_action (
1699 		const int id,
1700 		const std::string& name,
1701 		const std::string& script,
1702 		const LuaScriptParamList& args)
1703 {
1704 	try {
1705 		lua_State* L = lua.getState();
1706 		// get bytcode of factory-function in a sandbox
1707 		// (don't allow scripts to interfere)
1708 		const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
1709 		const std::string& iconfunc = LuaScripting::get_factory_bytecode (script, "icon", "icn");
1710 		luabridge::LuaRef tbl_arg (luabridge::newTable(L));
1711 		for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
1712 			if ((*i)->optional && !(*i)->is_set) { continue; }
1713 			tbl_arg[(*i)->name] = (*i)->value;
1714 		}
1715 		(*_lua_add_action)(id + 1, name, script, bytecode, iconfunc, tbl_arg);
1716 		ActionChanged (id, name); /* EMIT SIGNAL */
1717 	} catch (luabridge::LuaException const& e) {
1718 #ifndef NDEBUG
1719 		cerr << "LuaException:" << e.what () << endl;
1720 #endif
1721 		PBD::warning << "LuaException: " << e.what () << endmsg;
1722 		return false;
1723 	} catch (...) {
1724 		return false;
1725 	}
1726 	set_dirty ();
1727 	return true;
1728 }
1729 
1730 bool
remove_lua_action(const int id)1731 LuaInstance::remove_lua_action (const int id)
1732 {
1733 	try {
1734 		(*_lua_del_action)(id + 1);
1735 	} catch (luabridge::LuaException const& e) {
1736 #ifndef NDEBUG
1737 		cerr << "LuaException:" << e.what () << endl;
1738 #endif
1739 		PBD::warning << "LuaException: " << e.what () << endmsg;
1740 		return false;
1741 	} catch (...) {
1742 		return false;
1743 	}
1744 	ActionChanged (id, ""); /* EMIT SIGNAL */
1745 	set_dirty ();
1746 	return true;
1747 }
1748 
1749 bool
lua_action_name(const int id,std::string & rv)1750 LuaInstance::lua_action_name (const int id, std::string& rv)
1751 {
1752 	try {
1753 		luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
1754 		if (ref.isNil()) {
1755 			return false;
1756 		}
1757 		if (ref["name"].isString()) {
1758 			rv = ref["name"].cast<std::string>();
1759 			return true;
1760 		}
1761 		return true;
1762 	} catch (luabridge::LuaException const& e) {
1763 #ifndef NDEBUG
1764 		cerr << "LuaException:" << e.what () << endl;
1765 #endif
1766 		PBD::warning << "LuaException: " << e.what () << endmsg;
1767 	} catch (...) { }
1768 	return false;
1769 }
1770 
1771 std::vector<std::string>
lua_action_names()1772 LuaInstance::lua_action_names ()
1773 {
1774 	std::vector<std::string> rv;
1775 	for (int i = 0; i < MAX_LUA_ACTION_SCRIPTS; ++i) {
1776 		std::string name;
1777 		if (lua_action_name (i, name)) {
1778 			rv.push_back (name);
1779 		}
1780 	}
1781 	return rv;
1782 }
1783 
1784 bool
lua_action_has_icon(const int id)1785 LuaInstance::lua_action_has_icon (const int id)
1786 {
1787 	try {
1788 		luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
1789 		if (ref.isNil()) {
1790 			return false;
1791 		}
1792 		if (ref["icon"].isBoolean()) {
1793 			return ref["icon"].cast<bool>();
1794 		}
1795 	} catch (luabridge::LuaException const& e) {
1796 #ifndef NDEBUG
1797 		cerr << "LuaException:" << e.what () << endl;
1798 #endif
1799 		PBD::warning << "LuaException: " << e.what () << endmsg;
1800 	} catch (...) { }
1801 	return false;
1802 }
1803 
1804 bool
lua_action(const int id,std::string & name,std::string & script,LuaScriptParamList & args)1805 LuaInstance::lua_action (const int id, std::string& name, std::string& script, LuaScriptParamList& args)
1806 {
1807 	try {
1808 		luabridge::LuaRef ref ((*_lua_get_action)(id + 1));
1809 		if (ref.isNil()) {
1810 			return false;
1811 		}
1812 		if (!ref["name"].isString()) {
1813 			return false;
1814 		}
1815 		if (!ref["script"].isString()) {
1816 			return false;
1817 		}
1818 		if (!ref["args"].isTable()) {
1819 			return false;
1820 		}
1821 		name = ref["name"].cast<std::string>();
1822 		script = ref["script"].cast<std::string>();
1823 
1824 		args.clear();
1825 		LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
1826 		if (!lsi) {
1827 			return false;
1828 		}
1829 		args = LuaScriptParams::script_params (lsi, "action_params");
1830 		luabridge::LuaRef rargs (ref["args"]);
1831 		LuaScriptParams::ref_to_params (args, &rargs);
1832 		return true;
1833 	} catch (luabridge::LuaException const& e) {
1834 #ifndef NDEBUG
1835 		cerr << "LuaException:" << e.what () << endl;
1836 #endif
1837 		PBD::warning << "LuaException: " << e.what () << endmsg;
1838 	} catch (...) { }
1839 	return false;
1840 }
1841 
1842 bool
register_lua_slot(const std::string & name,const std::string & script,const ARDOUR::LuaScriptParamList & args)1843 LuaInstance::register_lua_slot (const std::string& name, const std::string& script, const ARDOUR::LuaScriptParamList& args)
1844 {
1845 	/* parse script, get ActionHook(s) from script */
1846 	ActionHook ah;
1847 	try {
1848 		LuaState l;
1849 		l.Print.connect (&_lua_print);
1850 		l.sandbox (true);
1851 		lua_State* L = l.getState();
1852 		register_hooks (L);
1853 		l.do_command ("function ardour () end");
1854 		l.do_command (script);
1855 		luabridge::LuaRef signals = luabridge::getGlobal (L, "signals");
1856 		if (signals.isFunction()) {
1857 			ah = signals();
1858 		}
1859 	} catch (luabridge::LuaException const& e) {
1860 #ifndef NDEBUG
1861 		cerr << "LuaException:" << e.what () << endl;
1862 #endif
1863 		PBD::warning << "LuaException: " << e.what () << endmsg;
1864 	} catch (...) { }
1865 
1866 	if (ah.none ()) {
1867 		cerr << "Script registered no hooks." << endl;
1868 		return false;
1869 	}
1870 
1871 	/* register script w/args, get entry-point / ID */
1872 
1873 	try {
1874 		LuaCallbackPtr p (new LuaCallback (_session, name, script, ah, args));
1875 		_callbacks.insert (std::make_pair(p->id(), p));
1876 		p->drop_callback.connect (_slotcon, MISSING_INVALIDATOR, boost::bind (&LuaInstance::unregister_lua_slot, this, p->id()), gui_context());
1877 		SlotChanged (p->id(), p->name(), p->signals()); /* EMIT SIGNAL */
1878 		set_dirty ();
1879 		return true;
1880 	} catch (luabridge::LuaException const& e) {
1881 #ifndef NDEBUG
1882 		cerr << "LuaException:" << e.what () << endl;
1883 #endif
1884 		PBD::warning << "LuaException: " << e.what () << endmsg;
1885 	} catch (...) { }
1886 	return false;
1887 }
1888 
1889 bool
unregister_lua_slot(const PBD::ID & id)1890 LuaInstance::unregister_lua_slot (const PBD::ID& id)
1891 {
1892 	LuaCallbackMap::iterator i = _callbacks.find (id);
1893 	if (i != _callbacks.end()) {
1894 		SlotChanged (id, "", ActionHook()); /* EMIT SIGNAL */
1895 		_callbacks.erase (i);
1896 		set_dirty ();
1897 		return true;
1898 	}
1899 	return false;
1900 }
1901 
1902 std::vector<PBD::ID>
lua_slots() const1903 LuaInstance::lua_slots () const
1904 {
1905 	std::vector<PBD::ID> rv;
1906 	for (LuaCallbackMap::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i) {
1907 		rv.push_back (i->first);
1908 	}
1909 	return rv;
1910 }
1911 
1912 bool
lua_slot_name(const PBD::ID & id,std::string & name) const1913 LuaInstance::lua_slot_name (const PBD::ID& id, std::string& name) const
1914 {
1915 	LuaCallbackMap::const_iterator i = _callbacks.find (id);
1916 	if (i != _callbacks.end()) {
1917 		name = i->second->name();
1918 		return true;
1919 	}
1920 	return false;
1921 }
1922 
1923 std::vector<std::string>
lua_slot_names() const1924 LuaInstance::lua_slot_names () const
1925 {
1926 	std::vector<std::string> rv;
1927 	std::vector<PBD::ID> ids = lua_slots();
1928 	for (std::vector<PBD::ID>::const_iterator i = ids.begin(); i != ids.end(); ++i) {
1929 		std::string name;
1930 		if (lua_slot_name (*i, name)) {
1931 			rv.push_back (name);
1932 		}
1933 	}
1934 	return rv;
1935 }
1936 
1937 bool
lua_slot(const PBD::ID & id,std::string & name,std::string & script,ActionHook & ah,ARDOUR::LuaScriptParamList & args)1938 LuaInstance::lua_slot (const PBD::ID& id, std::string& name, std::string& script, ActionHook& ah, ARDOUR::LuaScriptParamList& args)
1939 {
1940 	LuaCallbackMap::const_iterator i = _callbacks.find (id);
1941 	if (i == _callbacks.end()) {
1942 		return false; // error
1943 	}
1944 	return i->second->lua_slot (name, script, ah, args);
1945 }
1946 
1947 ///////////////////////////////////////////////////////////////////////////////
1948 
LuaCallback(Session * s,const std::string & name,const std::string & script,const ActionHook & ah,const ARDOUR::LuaScriptParamList & args)1949 LuaCallback::LuaCallback (Session *s,
1950 		const std::string& name,
1951 		const std::string& script,
1952 		const ActionHook& ah,
1953 		const ARDOUR::LuaScriptParamList& args)
1954 	: SessionHandlePtr (s)
1955 	, _id ("0")
1956 	, _name (name)
1957 	, _signals (ah)
1958 {
1959 	// TODO: allow to reference object (e.g region)
1960 	init ();
1961 
1962 	lua_State* L = lua.getState();
1963 	luabridge::LuaRef tbl_arg (luabridge::newTable(L));
1964 	for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
1965 		if ((*i)->optional && !(*i)->is_set) { continue; }
1966 		tbl_arg[(*i)->name] = (*i)->value;
1967 	}
1968 
1969 	try {
1970 		const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
1971 		(*_lua_add)(name, script, bytecode, tbl_arg);
1972 	} catch (luabridge::LuaException const& e) {
1973 #ifndef NDEBUG
1974 		cerr << "LuaException:" << e.what () << endl;
1975 #endif
1976 		PBD::warning << "LuaException: " << e.what () << endmsg;
1977 		throw failed_constructor ();
1978 	} catch (...) {
1979 		throw failed_constructor ();
1980 	}
1981 
1982 	_id.reset ();
1983 	set_session (s);
1984 }
1985 
LuaCallback(Session * s,XMLNode & node)1986 LuaCallback::LuaCallback (Session *s, XMLNode & node)
1987 	: SessionHandlePtr (s)
1988 {
1989 	XMLNode* child = NULL;
1990 	if (node.name() != X_("LuaCallback")
1991 			|| !node.property ("signals")
1992 			|| !node.property ("id")
1993 			|| !node.property ("name")) {
1994 		throw failed_constructor ();
1995 	}
1996 
1997 	for (XMLNodeList::const_iterator n = node.children ().begin (); n != node.children ().end (); ++n) {
1998 		if (!(*n)->is_content ()) { continue; }
1999 		child = *n;
2000 	}
2001 
2002 	if (!child) {
2003 		throw failed_constructor ();
2004 	}
2005 
2006 	init ();
2007 
2008 	_id = PBD::ID (node.property ("id")->value ());
2009 	_name = node.property ("name")->value ();
2010 	_signals = ActionHook (node.property ("signals")->value ());
2011 
2012 	gsize size;
2013 	guchar* buf = g_base64_decode (child->content ().c_str (), &size);
2014 	try {
2015 		(*_lua_load)(std::string ((const char*)buf, size));
2016 	} catch (luabridge::LuaException const& e) {
2017 #ifndef NDEBUG
2018 		cerr << "LuaException:" << e.what () << endl;
2019 #endif
2020 		PBD::warning << "LuaException: " << e.what () << endmsg;
2021 	} catch (...) { }
2022 	g_free (buf);
2023 
2024 	set_session (s);
2025 }
2026 
~LuaCallback()2027 LuaCallback::~LuaCallback ()
2028 {
2029 	delete _lua_add;
2030 	delete _lua_get;
2031 	delete _lua_call;
2032 	delete _lua_load;
2033 	delete _lua_save;
2034 }
2035 
2036 XMLNode&
get_state(void)2037 LuaCallback::get_state (void)
2038 {
2039 	std::string saved;
2040 	{
2041 		luabridge::LuaRef savedstate ((*_lua_save)());
2042 		saved = savedstate.cast<std::string>();
2043 	}
2044 
2045 	lua.collect_garbage (); // this may be expensive:
2046 	/* Editor::instant_save() calls Editor::get_state() which
2047 	 * calls LuaInstance::get_hook_state() which in turn calls
2048 	 * this LuaCallback::get_state() for every registered hook.
2049 	 *
2050 	 * serialize in _lua_save() allocates many small strings
2051 	 * on the lua-stack, collecting them all may take a ms.
2052 	 */
2053 
2054 	gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
2055 	std::string b64s (b64);
2056 	g_free (b64);
2057 
2058 	XMLNode* script_node = new XMLNode (X_("LuaCallback"));
2059 	script_node->set_property (X_("lua"), LUA_VERSION);
2060 	script_node->set_property (X_("id"), _id.to_s ());
2061 	script_node->set_property (X_("name"), _name);
2062 	script_node->set_property (X_("signals"), _signals.to_string ());
2063 	script_node->add_content (b64s);
2064 	return *script_node;
2065 }
2066 
2067 void
init(void)2068 LuaCallback::init (void)
2069 {
2070 	lua.Print.connect (&_lua_print);
2071 	lua.sandbox (false);
2072 
2073 	lua.do_command (
2074 			"function ScriptManager ()"
2075 			"  local self = { script = {}, instance = {} }"
2076 			""
2077 			"  local addinternal = function (n, s, f, a)"
2078 			"   assert(type(n) == 'string', 'Name must be string')"
2079 			"   assert(type(s) == 'string', 'Script must be string')"
2080 			"   assert(type(f) == 'function', 'Factory is a not a function')"
2081 			"   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
2082 			"   self.script = { ['n'] = n, ['s'] = s, ['f'] = f, ['a'] = a }"
2083 			"   local env = _ENV; env.f = nil"
2084 			"   self.instance = load (string.dump(f, true), nil, nil, env)(a)"
2085 			"  end"
2086 			""
2087 			"  local call = function (...)"
2088 			"   if type(self.instance) == 'function' then"
2089 			"     local status, err = pcall (self.instance, ...)"
2090 			"     if not status then"
2091 			"       print ('callback \"'.. self.script['n'] .. '\": ', err)" // error out
2092 			"       self.script = nil"
2093 			"       self.instance = nil"
2094 			"       return false"
2095 			"     end"
2096 			"   end"
2097 			"   collectgarbage()"
2098 			"   return true"
2099 			"  end"
2100 			""
2101 			"  local add = function (n, s, b, a)"
2102 			"   assert(type(b) == 'string', 'ByteCode must be string')"
2103 			"   load (b)()" // assigns f
2104 			"   assert(type(f) == 'string', 'Assigned ByteCode must be string')"
2105 			"   addinternal (n, s, load(f), a)"
2106 			"  end"
2107 			""
2108 			"  local get = function ()"
2109 			"   if type(self.instance) == 'function' and type(self.script['n']) == 'string' then"
2110 			"    return { ['name'] = self.script['n'],"
2111 			"             ['script'] = self.script['s'],"
2112 			"             ['args'] = self.script['a'] }"
2113 			"   end"
2114 			"   return nil"
2115 			"  end"
2116 			""
2117 			// code dup
2118 			""
2119 			"  local function basic_serialize (o)"
2120 			"    if type(o) == \"number\" then"
2121 			"     return tostring(o)"
2122 			"    else"
2123 			"     return string.format(\"%q\", o)"
2124 			"    end"
2125 			"  end"
2126 			""
2127 			"  local function serialize (name, value)"
2128 			"   local rv = name .. ' = '"
2129 			"   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
2130 			"    return rv .. basic_serialize(value) .. ' '"
2131 			"   elseif type(value) == \"table\" then"
2132 			"    rv = rv .. '{} '"
2133 			"    for k,v in pairs(value) do"
2134 			"     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
2135 			"     rv = rv .. serialize(fieldname, v) .. ' '"
2136 			"    end"
2137 			"    return rv;"
2138 			"   elseif type(value) == \"function\" then"
2139 			"     return rv .. string.format(\"%q\", string.dump(value, true))"
2140 			"   elseif type(value) == \"boolean\" then"
2141 			"     return rv .. tostring (value)"
2142 			"   else"
2143 			"    error('cannot save a ' .. type(value))"
2144 			"   end"
2145 			"  end"
2146 			""
2147 			// end code dup
2148 			""
2149 			"  local save = function ()"
2150 			"   return (serialize('s', self.script))"
2151 			"  end"
2152 			""
2153 			"  local restore = function (state)"
2154 			"   self.script = {}"
2155 			"   load (state)()"
2156 			"   addinternal (s['n'], s['s'], load(s['f']), s['a'])"
2157 			"  end"
2158 			""
2159 			" return { call = call, add = add, get = get,"
2160 			"          restore = restore, save = save}"
2161 			" end"
2162 			" "
2163 			" manager = ScriptManager ()"
2164 			" ScriptManager = nil"
2165 			);
2166 
2167 	lua_State* L = lua.getState();
2168 
2169 	try {
2170 		luabridge::LuaRef lua_mgr = luabridge::getGlobal (L, "manager");
2171 		lua.do_command ("manager = nil"); // hide it.
2172 		lua.do_command ("collectgarbage()");
2173 
2174 		_lua_add = new luabridge::LuaRef(lua_mgr["add"]);
2175 		_lua_get = new luabridge::LuaRef(lua_mgr["get"]);
2176 		_lua_call = new luabridge::LuaRef(lua_mgr["call"]);
2177 		_lua_save = new luabridge::LuaRef(lua_mgr["save"]);
2178 		_lua_load = new luabridge::LuaRef(lua_mgr["restore"]);
2179 
2180 	} catch (luabridge::LuaException const& e) {
2181 		fatal << string_compose (_("programming error: %1"),
2182 				std::string ("Failed to setup Lua callback interpreter: ") + e.what ())
2183 			<< endmsg;
2184 		abort(); /*NOTREACHED*/
2185 	} catch (...) {
2186 		fatal << string_compose (_("programming error: %1"),
2187 				X_("Failed to setup Lua callback interpreter"))
2188 			<< endmsg;
2189 		abort(); /*NOTREACHED*/
2190 	}
2191 
2192 	LuaInstance::register_classes (L);
2193 	LuaInstance::register_hooks (L);
2194 
2195 	luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
2196 	lua_setglobal (L, "Editor");
2197 }
2198 
2199 bool
lua_slot(std::string & name,std::string & script,ActionHook & ah,ARDOUR::LuaScriptParamList & args)2200 LuaCallback::lua_slot (std::string& name, std::string& script, ActionHook& ah, ARDOUR::LuaScriptParamList& args)
2201 {
2202 	// TODO consolidate w/ LuaInstance::lua_action()
2203 	try {
2204 		luabridge::LuaRef ref = (*_lua_get)();
2205 		if (ref.isNil()) {
2206 			return false;
2207 		}
2208 		if (!ref["name"].isString()) {
2209 			return false;
2210 		}
2211 		if (!ref["script"].isString()) {
2212 			return false;
2213 		}
2214 		if (!ref["args"].isTable()) {
2215 			return false;
2216 		}
2217 
2218 		ah = _signals;
2219 		name = ref["name"].cast<std::string> ();
2220 		script = ref["script"].cast<std::string> ();
2221 
2222 		args.clear();
2223 		LuaScriptInfoPtr lsi = LuaScripting::script_info (script);
2224 		if (!lsi) {
2225 			return false;
2226 		}
2227 		args = LuaScriptParams::script_params (lsi, "action_params");
2228 		luabridge::LuaRef rargs (ref["args"]);
2229 		LuaScriptParams::ref_to_params (args, &rargs);
2230 		return true;
2231 	} catch (luabridge::LuaException const& e) {
2232 #ifndef NDEBUG
2233 		cerr << "LuaException:" << e.what () << endl;
2234 #endif
2235 		PBD::warning << "LuaException: " << e.what () << endmsg;
2236 		return false;
2237 	} catch (...) { }
2238 	return false;
2239 }
2240 
2241 void
set_session(ARDOUR::Session * s)2242 LuaCallback::set_session (ARDOUR::Session *s)
2243 {
2244 	SessionHandlePtr::set_session (s);
2245 
2246 	if (!_session) {
2247 		return;
2248 	}
2249 
2250 	lua_State* L = lua.getState();
2251 	LuaBindings::set_session (L, _session);
2252 
2253 	reconnect();
2254 }
2255 
2256 void
session_going_away()2257 LuaCallback::session_going_away ()
2258 {
2259 	ENSURE_GUI_THREAD (*this, &LuaCallback::session_going_away);
2260 	lua.do_command ("collectgarbage();");
2261 
2262 	SessionHandlePtr::session_going_away ();
2263 	_session = 0;
2264 
2265 	drop_callback (); /* EMIT SIGNAL */
2266 }
2267 
2268 void
reconnect()2269 LuaCallback::reconnect ()
2270 {
2271 	_connections.drop_connections ();
2272 	if ((*_lua_get) ().isNil ()) {
2273 		drop_callback (); /* EMIT SIGNAL */
2274 		return;
2275 	}
2276 
2277 	// TODO pass object which emits the signal (e.g region)
2278 	//
2279 	// save/load bound objects will be tricky.
2280 	// Best idea so far is to save/lookup the PBD::ID
2281 	// (either use boost::any indirection or templates for bindable
2282 	// object types or a switch statement..)
2283 	//
2284 	// _session->route_by_id ()
2285 	// _session->track_by_diskstream_id ()
2286 	// _session->source_by_id ()
2287 	// _session->controllable_by_id ()
2288 	// _session->processor_by_id ()
2289 	// RegionFactory::region_by_id ()
2290 	//
2291 	// TODO loop over objects (if any)
2292 
2293 	reconnect_object ((void*)0);
2294 }
2295 
2296 template <class T> void
reconnect_object(T obj)2297 LuaCallback::reconnect_object (T obj)
2298 {
2299 	for (uint32_t i = 0; i < LuaSignal::LAST_SIGNAL; ++i) {
2300 		if (_signals[i]) {
2301 #define ENGINE(n,c,p) else if (i == LuaSignal::n) { connect_ ## p (LuaSignal::n, AudioEngine::instance(), &(AudioEngine::instance()->c)); }
2302 #define SESSION(n,c,p) else if (i == LuaSignal::n) { if (_session) { connect_ ## p (LuaSignal::n, _session, &(_session->c)); } }
2303 #define STATIC(n,c,p) else if (i == LuaSignal::n) { connect_ ## p (LuaSignal::n, obj, c); }
2304 			if (0) {}
2305 #			include "luasignal_syms.h"
2306 			else {
2307 				PBD::fatal << string_compose (_("programming error: %1: %2"), "Impossible LuaSignal type", i) << endmsg;
2308 				abort(); /*NOTREACHED*/
2309 			}
2310 #undef ENGINE
2311 #undef SESSION
2312 #undef STATIC
2313 		}
2314 	}
2315 }
2316 
2317 template <typename T, typename S> void
connect_0(enum LuaSignal::LuaSignal ls,T ref,S * signal)2318 LuaCallback::connect_0 (enum LuaSignal::LuaSignal ls, T ref, S *signal) {
2319 	signal->connect (
2320 			_connections, invalidator (*this),
2321 			boost::bind (&LuaCallback::proxy_0<T>, this, ls, ref),
2322 			gui_context());
2323 }
2324 
2325 template <typename T, typename C1> void
connect_1(enum LuaSignal::LuaSignal ls,T ref,PBD::Signal1<void,C1> * signal)2326 LuaCallback::connect_1 (enum LuaSignal::LuaSignal ls, T ref, PBD::Signal1<void, C1> *signal) {
2327 	signal->connect (
2328 			_connections, invalidator (*this),
2329 			boost::bind (&LuaCallback::proxy_1<T, C1>, this, ls, ref, _1),
2330 			gui_context());
2331 }
2332 
2333 template <typename T, typename C1, typename C2> void
connect_2(enum LuaSignal::LuaSignal ls,T ref,PBD::Signal2<void,C1,C2> * signal)2334 LuaCallback::connect_2 (enum LuaSignal::LuaSignal ls, T ref, PBD::Signal2<void, C1, C2> *signal) {
2335 	signal->connect (
2336 			_connections, invalidator (*this),
2337 			boost::bind (&LuaCallback::proxy_2<T, C1, C2>, this, ls, ref, _1, _2),
2338 			gui_context());
2339 }
2340 
2341 template <typename T, typename C1, typename C2, typename C3> void
connect_3(enum LuaSignal::LuaSignal ls,T ref,PBD::Signal3<void,C1,C2,C3> * signal)2342 LuaCallback::connect_3 (enum LuaSignal::LuaSignal ls, T ref, PBD::Signal3<void, C1, C2, C3> *signal) {
2343 	signal->connect (
2344 			_connections, invalidator (*this),
2345 			boost::bind (&LuaCallback::proxy_3<T, C1, C2, C3>, this, ls, ref, _1, _2, _3),
2346 			gui_context());
2347 }
2348 
2349 template <typename T> void
proxy_0(enum LuaSignal::LuaSignal ls,T ref)2350 LuaCallback::proxy_0 (enum LuaSignal::LuaSignal ls, T ref) {
2351 	bool ok = true;
2352 	{
2353 		const luabridge::LuaRef& rv ((*_lua_call)((int)ls, ref));
2354 		if (! rv.cast<bool> ()) {
2355 			ok = false;
2356 		}
2357 	}
2358 	/* destroy LuaRef ^^ first before calling drop_callback() */
2359 	if (!ok) {
2360 		drop_callback (); /* EMIT SIGNAL */
2361 	}
2362 }
2363 
2364 template <typename T, typename C1> void
proxy_1(enum LuaSignal::LuaSignal ls,T ref,C1 a1)2365 LuaCallback::proxy_1 (enum LuaSignal::LuaSignal ls, T ref, C1 a1) {
2366 	bool ok = true;
2367 	{
2368 		const luabridge::LuaRef& rv ((*_lua_call)((int)ls, ref, a1));
2369 		if (! rv.cast<bool> ()) {
2370 			ok = false;
2371 		}
2372 	}
2373 	if (!ok) {
2374 		drop_callback (); /* EMIT SIGNAL */
2375 	}
2376 }
2377 
2378 template <typename T, typename C1, typename C2> void
proxy_2(enum LuaSignal::LuaSignal ls,T ref,C1 a1,C2 a2)2379 LuaCallback::proxy_2 (enum LuaSignal::LuaSignal ls, T ref, C1 a1, C2 a2) {
2380 	bool ok = true;
2381 	{
2382 		const luabridge::LuaRef& rv ((*_lua_call)((int)ls, ref, a1, a2));
2383 		if (! rv.cast<bool> ()) {
2384 			ok = false;
2385 		}
2386 	}
2387 	if (!ok) {
2388 		drop_callback (); /* EMIT SIGNAL */
2389 	}
2390 }
2391 
2392 template <typename T, typename C1, typename C2, typename C3> void
proxy_3(enum LuaSignal::LuaSignal ls,T ref,C1 a1,C2 a2,C3 a3)2393 LuaCallback::proxy_3 (enum LuaSignal::LuaSignal ls, T ref, C1 a1, C2 a2, C3 a3) {
2394 	bool ok = true;
2395 	{
2396 		const luabridge::LuaRef& rv ((*_lua_call)((int)ls, ref, a1, a2, a3));
2397 		if (! rv.cast<bool> ()) {
2398 			ok = false;
2399 		}
2400 	}
2401 	if (!ok) {
2402 		drop_callback (); /* EMIT SIGNAL */
2403 	}
2404 }
2405