0b44ee80515b1464471cab567fb90d527a97516d
[ardour.git] / libs / pbd / pbd / signals.h
1 /*
2     Copyright (C) 2009 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef __pbd_signals_h__
21 #define __pbd_signals_h__
22
23 #include <list>
24 #include <glibmm/thread.h>
25
26 #include <boost/signals2.hpp>
27 #include <boost/noncopyable.hpp>
28 #include <boost/bind.hpp>
29 #include <boost/bind/protect.hpp>
30
31 #include "pbd/event_loop.h"
32
33 namespace PBD {
34
35 typedef boost::signals2::connection UnscopedConnection;
36 typedef boost::signals2::connection Connection;
37 typedef boost::signals2::scoped_connection ScopedConnection;
38
39 class ScopedConnectionList  : public boost::noncopyable
40 {
41   public:
42         ScopedConnectionList();
43         ~ScopedConnectionList ();
44         
45         void add_connection (const UnscopedConnection& c);
46         void drop_connections ();
47
48   private:
49         /* this class is not copyable */
50         ScopedConnectionList(const ScopedConnectionList&);
51
52         /* this lock is shared by all instances of a ScopedConnectionList.
53            We do not want one mutex per list, and since we only need the lock
54            when adding or dropping connections, which are generally occuring
55            in object creation and UI operations, the contention on this 
56            lock is low and not of significant consequence. Even though
57            boost::signals2 is thread-safe, this additional list of
58            scoped connections needs to be protected in 2 cases:
59
60            (1) (unlikely) we make a connection involving a callback on the
61                same object from 2 threads. (wouldn't that just be appalling 
62                programming style?)
63              
64            (2) where we are dropping connections in one thread and adding
65                one from another.
66          */
67
68         static Glib::StaticMutex _lock;
69
70         typedef std::list<ScopedConnection*> ConnectionList;
71         ConnectionList _list;
72 };
73
74 template<typename R>
75 class Signal0 {
76 public:
77     Signal0 () {}
78     typedef boost::signals2::signal<R()> SignalType;
79
80     void connect_same_thread (Connection& c, 
81                   const typename SignalType::slot_function_type& slot) {
82             c = _signal.connect (slot);
83     }
84
85     void connect_same_thread (ScopedConnectionList& clist, 
86                   const typename SignalType::slot_function_type& slot) {
87             clist.add_connection (_signal.connect (slot));
88     }
89
90     void connect (ScopedConnectionList& clist, 
91                   const typename SignalType::slot_function_type& slot,
92                   PBD::EventLoop* event_loop) {
93             clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, slot)));
94     }
95     
96     void connect (Connection& c, 
97                   const typename SignalType::slot_function_type& slot,
98                   PBD::EventLoop* event_loop) {
99             c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, slot));
100     }
101     
102     typename SignalType::result_type operator()() {
103             return _signal ();
104     }
105     
106 private:
107     SignalType _signal;
108 };
109
110 template<typename R, typename A>
111 class Signal1 {
112 public:
113     Signal1 () {}
114     typedef boost::signals2::signal<R(A)> SignalType;
115
116     void connect_same_thread (ScopedConnectionList& clist, 
117                   const typename SignalType::slot_function_type& slot) {
118             clist.add_connection (_signal.connect (slot));
119     }
120
121     void connect_same_thread (Connection& c, 
122                      const typename SignalType::slot_function_type& slot) {
123             c = _signal.connect (slot);
124     }
125
126     static void compositor (typename boost::function<void(A)> f, EventLoop* event_loop, A arg) {
127             event_loop->call_slot (boost::bind (f, arg));
128     }
129
130     void connect (ScopedConnectionList& clist, 
131                   const typename SignalType::slot_function_type& slot,
132                   PBD::EventLoop* event_loop) {
133             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, _1)));
134     }
135
136     void connect (Connection& c, 
137                   const typename SignalType::slot_function_type& slot,
138                   PBD::EventLoop* event_loop) {
139             c = _signal.connect (boost::bind (&compositor, slot, event_loop, _1));
140
141     }
142     
143     typename SignalType::result_type operator()(A arg1) {
144             return _signal (arg1);
145     }
146     
147 private:
148     SignalType _signal;
149 };
150
151 template<typename R, typename A1, typename A2>
152 class Signal2 {
153 public:
154     Signal2 () {}
155     typedef boost::signals2::signal<R(A1, A2)> SignalType;
156
157     void connect_same_thread (ScopedConnectionList& clist, 
158                   const typename SignalType::slot_function_type& slot) {
159             clist.add_connection (_signal.connect (slot));
160     }
161
162     void connect_same_thread (Connection& c, 
163                      const typename SignalType::slot_function_type& slot) {
164             c = _signal.connect (slot);
165     }
166
167     static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop, A1 arg1, A2 arg2) {
168             event_loop->call_slot (boost::bind (f, arg1, arg2));
169     }
170
171     void connect (ScopedConnectionList& clist, 
172                   const typename SignalType::slot_function_type& slot,
173                   PBD::EventLoop* event_loop) {
174             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, _1, _2)));
175     }
176
177     void connect (Connection& c, 
178                   const typename SignalType::slot_function_type& slot,
179                   PBD::EventLoop* event_loop) {
180             c = _signal.connect (boost::bind (&compositor, slot, event_loop, _1, _2));
181     }
182
183     typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
184             return _signal (arg1, arg2);
185     }
186     
187 private:
188     SignalType _signal;
189 };
190
191 template<typename R, typename A1, typename A2, typename A3>
192 class Signal3 {
193 public:
194     Signal3 () {}
195     typedef boost::signals2::signal<R(A1,A2,A3)> SignalType;
196
197     void connect_same_thread (ScopedConnectionList& clist, 
198                   const typename SignalType::slot_function_type& slot) {
199             clist.add_connection (_signal.connect (slot));
200     }
201
202     void connect_same_thread (Connection& c, 
203                               const typename SignalType::slot_function_type& slot) {
204             c = _signal.connect (slot);
205     }
206
207     static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop, A1 arg1, A2 arg2, A3 arg3) {
208             event_loop->call_slot (boost::bind (f, arg1, arg2, arg3));
209     }
210
211     void connect (ScopedConnectionList& clist, 
212                   const typename SignalType::slot_function_type& slot,
213                   PBD::EventLoop* event_loop) {
214             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, _1, _2, _3)));
215     }
216     
217     void connect (Connection& c, 
218                   const typename SignalType::slot_function_type& slot,
219                   PBD::EventLoop* event_loop) {
220             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, _1, _2, _3)));
221     }
222     
223     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
224             return _signal (arg1, arg2, arg3);
225     }
226     
227 private:
228     SignalType _signal;
229 };
230
231 } /* namespace */
232
233 #endif /* __pbd_signals_h__ */