summaryrefslogtreecommitdiff
path: root/libs/pbd/pbd/signals.py
diff options
context:
space:
mode:
authorCarl Hetherington <carl@carlh.net>2012-05-15 00:06:17 +0000
committerCarl Hetherington <carl@carlh.net>2012-05-15 00:06:17 +0000
commit5bbbc985338aec965237e9fdf3696ddff2bc1002 (patch)
treedd72dce68686a5cb7a576070a5a40925984f0eee /libs/pbd/pbd/signals.py
parent32bed9aaf040cbd06f9855b568c14cb7a7050d58 (diff)
Add missing file.
git-svn-id: svn://localhost/ardour2/branches/3.0@12280 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/pbd/pbd/signals.py')
-rw-r--r--libs/pbd/pbd/signals.py298
1 files changed, 298 insertions, 0 deletions
diff --git a/libs/pbd/pbd/signals.py b/libs/pbd/pbd/signals.py
new file mode 100644
index 0000000000..e5ea09ae2e
--- /dev/null
+++ b/libs/pbd/pbd/signals.py
@@ -0,0 +1,298 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2009-2012 Paul Davis
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+#
+# This file generates the header signals_generated.h, which
+# will be put in build/libs/pbd/pbd by waf.
+#
+# It is probably easier to read build/libs/pbd/pbd/signals_generated.h
+# than this if you want to read the code!
+#
+
+import sys
+
+if len(sys.argv) < 2:
+ print 'Syntax: %s <path>' % sys.argv[0]
+ sys.exit(1)
+
+f = open(sys.argv[1], 'w')
+
+print >>f,"/** THIS FILE IS AUTOGENERATED by signals.py: CHANGES WILL BE LOST */\n\n"
+
+# Produce a comma-separated string from a list of substrings,
+# giving an optional prefix to each substring
+def comma_separated(n, prefix = ""):
+ r = ""
+ for i in range(0, len(n)):
+ if i > 0:
+ r += ", "
+ r += "%s%s" % (prefix, n[i])
+ return r
+
+# Generate one SignalN class definition
+# @param f File to write to
+# @param n Number of parameters
+# @param v True to specialize the template for a void return type
+def signal(f, n, v):
+
+ # The parameters in the form A1, A2, A3, ...
+ An = []
+ for i in range(0, n):
+ An.append("A%d" % (i + 1))
+
+ # The parameters in the form A1 a1, A2 a2, A3 a3, ...
+ Anan = []
+ for a in An:
+ Anan.append('%s %s' % (a, a.lower()))
+
+ # The parameters in the form a1, a2, a3, ...
+ an = []
+ for a in An:
+ an.append(a.lower())
+
+ if v:
+ print >>f,"template <%s>" % comma_separated(An, "typename ")
+ print >>f,"class Signal%d<%s> : public SignalBase" % (n, comma_separated(["void"] + An))
+ else:
+ print >>f,"template <%s>" % comma_separated(["R"] + An + ["C = OptionalLastValue<R> "], "typename ")
+ print >>f,"class Signal%d : public SignalBase" % n
+
+ print >>f,"{"
+ print >>f,"public:"
+ print >>f,""
+ if v:
+ print >>f,"\ttypedef boost::function<void(%s)> slot_function_type;" % comma_separated(An)
+ print >>f,"\ttypedef void result_type;"
+ else:
+ print >>f,"\ttypedef boost::function<R(%s)> slot_function_type;" % comma_separated(An)
+ print >>f,"\ttypedef boost::optional<R> result_type;"
+
+ print >>f,""
+
+ print >>f,"private:"
+
+ print >>f,"""
+ typedef std::map<boost::shared_ptr<Connection>, slot_function_type> Slots;
+ Slots _slots;
+"""
+
+ print >>f,"public:"
+ print >>f,""
+ print >>f,"\t~Signal%d () {" % n,
+
+ print >>f,"""
+ boost::mutex::scoped_lock lm (_mutex);
+#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6))
+ for (typename Slots::iterator i = _slots.begin(); i != _slots.end(); ++i) {
+#else
+ for (Slots::iterator i = _slots.begin(); i != _slots.end(); ++i) {
+#endif
+ i->first->signal_going_away ();
+ }
+ }
+"""
+
+ if n == 0:
+ p = ""
+ q = ""
+ else:
+ p = ", %s" % comma_separated(Anan)
+ q = ", %s" % comma_separated(an)
+
+ print >>f,"\tstatic void compositor (typename boost::function<void(%s)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir%s) {" % (comma_separated(An), p)
+ print >>f,"\t\tevent_loop->call_slot (ir, boost::bind (f%s));" % q
+ print >>f,"\t}"
+
+ print >>f,"""
+
+ /** Arrange for @a slot to be executed whenever this signal is emitted.
+ Store the connection that represents this arrangement in @a c.
+
+ NOTE: @a slot will be executed in the same thread that the signal is
+ emitted in.
+ */
+
+ void connect_same_thread (ScopedConnection& c, const slot_function_type& slot) {
+ c = _connect (slot);
+ }
+
+ /** Arrange for @a slot to be executed whenever this signal is emitted.
+ Add the connection that represents this arrangement to @a clist.
+
+ NOTE: @a slot will be executed in the same thread that the signal is
+ emitted in.
+ */
+
+ void connect_same_thread (ScopedConnectionList& clist, const slot_function_type& slot) {
+ clist.add_connection (_connect (slot));
+ }
+
+ /** Arrange for @a slot to be executed in the context of @a event_loop
+ whenever this signal is emitted. Add the connection that represents
+ this arrangement to @a clist.
+
+ If the event loop/thread in which @a slot will be executed will
+ outlive the lifetime of any object referenced in @a slot,
+ then an InvalidationRecord should be passed, allowing
+ any request sent to the @a event_loop and not executed
+ before the object is destroyed to be marked invalid.
+
+ "outliving the lifetime" doesn't have a specific, detailed meaning,
+ but is best illustrated by two contrasting examples:
+
+ 1) the main GUI event loop/thread - this will outlive more or
+ less all objects in the application, and thus when arranging for
+ @a slot to be called in that context, an invalidation record is
+ highly advisable.
+
+ 2) a secondary event loop/thread which will be destroyed along
+ with the objects that are typically referenced by @a slot.
+ Assuming that the event loop is stopped before the objects are
+ destroyed, there is no reason to pass in an invalidation record,
+ and MISSING_INVALIDATOR may be used.
+ */
+
+ void connect (ScopedConnectionList& clist,
+ PBD::EventLoop::InvalidationRecord* ir,
+ const slot_function_type& slot,
+ PBD::EventLoop* event_loop) {
+
+ if (ir) {
+ ir->event_loop = event_loop;
+ }
+"""
+ u = []
+ for i in range(0, n):
+ u.append("_%d" % (i + 1))
+
+ if n == 0:
+ p = ""
+ else:
+ p = ", %s" % comma_separated(u)
+
+ print >>f,"\t\tclist.add_connection (_connect (boost::bind (&compositor, slot, event_loop, ir%s)));" % p
+
+ print >>f,"""
+ }
+
+ /** See notes for the ScopedConnectionList variant of this function. This
+ * differs in that it stores the connection to the signal in a single
+ * ScopedConnection rather than a ScopedConnectionList.
+ */
+
+ void connect (ScopedConnection& c,
+ PBD::EventLoop::InvalidationRecord* ir,
+ const slot_function_type& slot,
+ PBD::EventLoop* event_loop) {
+
+ if (ir) {
+ ir->event_loop = event_loop;
+ }
+"""
+ print >>f,"\t\tc = _connect (boost::bind (&compositor, slot, event_loop, ir%s));" % p
+ print >>f,"\t}"
+
+ print >>f,"""
+ /** Emit this signal. This will cause all slots connected to it be executed
+ in the order that they were connected (cross-thread issues may alter
+ the precise execution time of cross-thread slots).
+ */
+"""
+
+ if v:
+ print >>f,"\tvoid operator() (%s)" % comma_separated(Anan)
+ else:
+ print >>f,"\ttypename C::result_type operator() (%s)" % comma_separated(Anan)
+ print >>f,"\t{"
+ print >>f,"\t\tSlots s;"
+ print >>f,"\t\t{"
+ print >>f,"\t\t\tboost::mutex::scoped_lock lm (_mutex);"
+ print >>f,"\t\t\ts = _slots;"
+ print >>f,"\t\t}"
+ if not v:
+ print >>f,"\t\tstd::list<R> r;"
+ if n == 0 and v:
+ print >>f,"""
+#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6))
+ for (typename Slots::iterator i = s.begin(); i != s.end(); ++i) {
+#else
+ for (Slots::iterator i = s.begin(); i != s.end(); ++i) {
+#endif
+"""
+ else:
+ print >>f,"\t\tfor (typename Slots::iterator i = s.begin(); i != s.end(); ++i) {"
+
+ print >>f,"""
+ bool still_there = false;
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ still_there = _slots.find (i->first) != _slots.end ();
+ }
+
+ if (still_there) {"""
+ if v:
+ print >>f,"\t\t\t\t(i->second)(%s);" % comma_separated(an)
+ else:
+ print >>f,"\t\t\t\tr.push_back ((i->second)(%s));" % comma_separated(an)
+ print >>f,"\t\t\t}"
+ print >>f,"\t\t}"
+ if not v:
+ print >>f,"\t\tC c;"
+ print >>f,"\t\treturn c (r.begin(), r.end());"
+ print >>f,"\t}"
+
+ print >>f,"""
+ bool empty () {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _slots.empty ();
+ }
+"""
+
+ if v:
+ tp = comma_separated(["void"] + An)
+ else:
+ tp = comma_separated(["R"] + An + ["C"])
+
+ print >>f,""
+ print >>f,"private:"
+ print >>f,""
+ print >>f,"\tfriend class Connection;"
+
+ print >>f,"""
+ boost::shared_ptr<Connection> _connect (slot_function_type f)
+ {
+ boost::shared_ptr<Connection> c (new Connection (this));
+ boost::mutex::scoped_lock lm (_mutex);
+ _slots[c] = f;
+ return c;
+ }
+"""
+
+ print >>f,"""
+ void disconnect (boost::shared_ptr<Connection> c)
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _slots.erase (c);
+ }
+};
+"""
+
+for i in range(0, 6):
+ signal(f, i, False)
+ signal(f, i, True)