diff options
author | Carl Hetherington <carl@carlh.net> | 2012-05-15 00:06:17 +0000 |
---|---|---|
committer | Carl Hetherington <carl@carlh.net> | 2012-05-15 00:06:17 +0000 |
commit | 5bbbc985338aec965237e9fdf3696ddff2bc1002 (patch) | |
tree | dd72dce68686a5cb7a576070a5a40925984f0eee | |
parent | 32bed9aaf040cbd06f9855b568c14cb7a7050d58 (diff) |
Add missing file.
git-svn-id: svn://localhost/ardour2/branches/3.0@12280 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | libs/pbd/pbd/signals.py | 298 |
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) |