8e2b120b10b3f948dfdbbdc940e2c17b0a585a9b
[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                   PBD::EventLoop::InvalidationRecord* ir, 
92                   const typename SignalType::slot_function_type& slot,
93                   PBD::EventLoop* event_loop) {
94             clist.add_connection (_signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot)));
95     }
96     
97     void connect (Connection& c, 
98                   PBD::EventLoop::InvalidationRecord* ir, 
99                   const typename SignalType::slot_function_type& slot,
100                   PBD::EventLoop* event_loop) {
101             c = _signal.connect (boost::bind (&EventLoop::call_slot, event_loop, ir, slot));
102     }
103     
104     typename SignalType::result_type operator()() {
105             return _signal ();
106     }
107
108     bool empty() const { return _signal.empty(); }
109     
110 private:
111     SignalType _signal;
112 };
113
114 template<typename R, typename A>
115 class Signal1 {
116 public:
117     Signal1 () {}
118     typedef boost::signals2::signal<R(A)> SignalType;
119
120     void connect_same_thread (ScopedConnectionList& clist, 
121                   const typename SignalType::slot_function_type& slot) {
122             clist.add_connection (_signal.connect (slot));
123     }
124
125     void connect_same_thread (Connection& c, 
126                      const typename SignalType::slot_function_type& slot) {
127             c = _signal.connect (slot);
128     }
129
130     static void compositor (typename boost::function<void(A)> f, EventLoop* event_loop, EventLoop::InvalidationRecord* ir, A arg) {
131             event_loop->call_slot (ir, boost::bind (f, arg));
132     }
133
134     void connect (ScopedConnectionList& clist, 
135                   PBD::EventLoop::InvalidationRecord* ir, 
136                   const typename SignalType::slot_function_type& slot,
137                   PBD::EventLoop* event_loop) {
138             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1)));
139     }
140
141     void connect (Connection& c, 
142                   PBD::EventLoop::InvalidationRecord* ir, 
143                   const typename SignalType::slot_function_type& slot,
144                   PBD::EventLoop* event_loop) {
145             c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1));
146
147     }
148     
149     typename SignalType::result_type operator()(A arg1) {
150             return _signal (arg1);
151     }
152     
153     bool empty() const { return _signal.empty(); }
154
155 private:
156     SignalType _signal;
157 };
158
159 template<typename R, typename A1, typename A2>
160 class Signal2 {
161 public:
162     Signal2 () {}
163     typedef boost::signals2::signal<R(A1, A2)> SignalType;
164
165     void connect_same_thread (ScopedConnectionList& clist, 
166                   const typename SignalType::slot_function_type& slot) {
167             clist.add_connection (_signal.connect (slot));
168     }
169
170     void connect_same_thread (Connection& c, 
171                      const typename SignalType::slot_function_type& slot) {
172             c = _signal.connect (slot);
173     }
174
175     static void compositor (typename boost::function<void(A1,A2)> f, PBD::EventLoop* event_loop, 
176                             EventLoop::InvalidationRecord* ir,
177                             A1 arg1, A2 arg2) {
178             event_loop->call_slot (ir, boost::bind (f, arg1, arg2));
179     }
180
181     void connect (ScopedConnectionList& clist, 
182                   PBD::EventLoop::InvalidationRecord* ir, 
183                   const typename SignalType::slot_function_type& slot,
184                   PBD::EventLoop* event_loop) {
185             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2)));
186     }
187
188     void connect (Connection& c, 
189                   PBD::EventLoop::InvalidationRecord* ir, 
190                   const typename SignalType::slot_function_type& slot,
191                   PBD::EventLoop* event_loop) {
192             c = _signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2));
193     }
194
195     typename SignalType::result_type operator()(A1 arg1, A2 arg2) {
196             return _signal (arg1, arg2);
197     }
198     
199     bool empty() const { return _signal.empty(); }
200
201 private:
202     SignalType _signal;
203 };
204
205 template<typename R, typename A1, typename A2, typename A3>
206 class Signal3 {
207 public:
208     Signal3 () {}
209     typedef boost::signals2::signal<R(A1,A2,A3)> SignalType;
210
211     void connect_same_thread (ScopedConnectionList& clist, 
212                   const typename SignalType::slot_function_type& slot) {
213             clist.add_connection (_signal.connect (slot));
214     }
215
216     void connect_same_thread (Connection& c, 
217                               const typename SignalType::slot_function_type& slot) {
218             c = _signal.connect (slot);
219     }
220
221     static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop, 
222                             EventLoop::InvalidationRecord* ir, 
223                             A1 arg1, A2 arg2, A3 arg3) {
224             event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3));
225     }
226
227     void connect (ScopedConnectionList& clist, 
228                   PBD::EventLoop::InvalidationRecord* ir, 
229                   const typename SignalType::slot_function_type& slot,
230                   PBD::EventLoop* event_loop) {
231             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
232     }
233     
234     void connect (Connection& c, 
235                   PBD::EventLoop::InvalidationRecord* ir, 
236                   const typename SignalType::slot_function_type& slot,
237                   PBD::EventLoop* event_loop) {
238             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3)));
239     }
240     
241     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3) {
242             return _signal (arg1, arg2, arg3);
243     }
244     
245     bool empty() const { return _signal.empty(); }
246
247 private:
248     SignalType _signal;
249 };
250
251 template<typename R, typename A1, typename A2, typename A3, typename A4>
252 class Signal4 {
253 public:
254     Signal4 () {}
255     typedef boost::signals2::signal<R(A1,A2,A3,A4)> SignalType;
256
257     void connect_same_thread (ScopedConnectionList& clist, 
258                   const typename SignalType::slot_function_type& slot) {
259             clist.add_connection (_signal.connect (slot));
260     }
261
262     void connect_same_thread (Connection& c, 
263                               const typename SignalType::slot_function_type& slot) {
264             c = _signal.connect (slot);
265     }
266
267     static void compositor (typename boost::function<void(A1,A2,A3)> f, PBD::EventLoop* event_loop, 
268                             EventLoop::InvalidationRecord* ir, 
269                             A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
270             event_loop->call_slot (ir, boost::bind (f, arg1, arg2, arg3, arg4));
271     }
272
273     void connect (ScopedConnectionList& clist, 
274                   PBD::EventLoop::InvalidationRecord* ir, 
275                   const typename SignalType::slot_function_type& slot,
276                   PBD::EventLoop* event_loop) {
277             clist.add_connection (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
278     }
279     
280     void connect (Connection& c, 
281                   PBD::EventLoop::InvalidationRecord* ir, 
282                   const typename SignalType::slot_function_type& slot,
283                   PBD::EventLoop* event_loop) {
284             c = _signal.connect (_signal.connect (boost::bind (&compositor, slot, event_loop, ir, _1, _2, _3, _4)));
285     }
286     
287     typename SignalType::result_type operator()(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
288             return _signal (arg1, arg2, arg3, arg4);
289     }
290     
291     bool empty() const { return _signal.empty(); }
292
293 private:
294     SignalType _signal;
295 };
296
297 } /* namespace */
298
299 #endif /* __pbd_signals_h__ */