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