cross-thread handling of SessionEvent allocation/deallocation, with widespread conseq...
[ardour.git] / libs / surfaces / wiimote / wiimote.cc
1 #include "wiimote.h"
2
3 #include <iostream>
4 #include <sigc++/bind.h>
5
6 #include "pbd/xml++.h"
7 #include "ardour/session.h"
8
9 #include "i18n.h"
10
11
12 using namespace ARDOUR;
13 using namespace PBD;
14
15 void wiimote_control_protocol_cwiid_callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], struct timespec *t);
16
17 uint16_t WiimoteControlProtocol::button_state = 0;
18
19 WiimoteControlProtocol::WiimoteControlProtocol ( Session & session) 
20         : ControlProtocol ( session, "Wiimote"),
21           main_thread_quit (false),
22           restart_discovery (false),
23           callback_thread_registered_for_ardour (false),
24           wiimote_handle (0)
25 {
26         main_thread = Glib::Thread::create( sigc::mem_fun(*this, &WiimoteControlProtocol::wiimote_main), true);
27 }
28
29 WiimoteControlProtocol::~WiimoteControlProtocol()
30 {
31         main_thread_quit = true;
32         slot_cond.signal();
33         main_thread->join();
34
35         if (wiimote_handle) {
36                 cwiid_close(wiimote_handle);
37         }
38
39         std::cerr << "Wiimote: closed" << std::endl;
40 }
41
42 bool 
43 WiimoteControlProtocol::probe()
44 {
45         return true;
46 }
47
48 void
49 WiimoteControlProtocol::wiimote_callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], struct timespec *t)
50 {
51         int i;
52         uint16_t b;
53
54         if (!callback_thread_registered_for_ardour) {
55                 register_thread("Wiimote Control Protocol");
56                 callback_thread_registered_for_ardour = true;
57         }
58
59         for (i=0; i < mesg_count; i++)
60         {
61                 if (mesg[i].type == CWIID_MESG_ERROR) { 
62                         std::cerr << "Wiimote: disconnect" << std::endl;
63                         restart_discovery = true;
64                         slot_cond.signal();
65                         return;
66                 }
67
68                 if (mesg[i].type != CWIID_MESG_BTN) continue;
69
70                 // what buttons are pressed down which weren't pressed down last time
71                 b = (mesg[i].btn_mesg.buttons ^ button_state) & mesg[i].btn_mesg.buttons;
72
73                 button_state = mesg[i].btn_mesg.buttons;
74         
75                 // if B is pressed down
76                 if (button_state & CWIID_BTN_B) {
77                         if (b & CWIID_BTN_A) { // B is down and A is pressed
78                                 access_action("Transport/ToggleRollForgetCapture");
79                         }
80
81                         if (b & CWIID_BTN_LEFT) {
82                                 access_action("Editor/playhead-to-previous-region-boundary");
83                         }
84                         if (b & CWIID_BTN_RIGHT) {
85                                 access_action("Editor/playhead-to-next-region-boundary");
86                         }
87                         if (b & CWIID_BTN_UP) {
88                                 next_marker();
89                         }
90                         if (b & CWIID_BTN_DOWN) {
91                                 prev_marker();
92                         }
93
94                         if (b & CWIID_BTN_HOME) {
95                                 access_action("Editor/add-location-from-playhead");
96                         }
97
98                         if (b & CWIID_BTN_MINUS) {      
99                                 access_action("Transport/GotoStart");
100                         }
101                         
102                         if (b & CWIID_BTN_PLUS) {
103                                 access_action("Transport/GotoEnd");
104                         }
105
106                         continue;
107                 }
108
109
110                 if (b & CWIID_BTN_A) {
111                         access_action("Transport/ToggleRoll");
112                 }
113
114                 if (b & CWIID_BTN_1) { // 1
115                         access_action("Editor/track-record-enable-toggle");
116                 }
117                 if (b & CWIID_BTN_2) { // 2
118                         rec_enable_toggle();
119                 }
120
121                 // d-pad
122                 if (b & CWIID_BTN_LEFT) { // left
123                         access_action("Editor/nudge-playhead-backward");
124                 }
125                 if (b & CWIID_BTN_RIGHT) { // right
126                         access_action("Editor/nudge-playhead-forward");
127                 }
128                 if (b & CWIID_BTN_DOWN) { // down
129                         access_action("Editor/select-next-route");
130                 }
131                 if (b & CWIID_BTN_UP) { // up
132                         access_action("Editor/select-prev-route");
133                 }
134
135
136                 if (b & CWIID_BTN_PLUS) { // +
137                         access_action("Editor/temporal-zoom-in");
138                 }
139                 if (b & CWIID_BTN_MINUS) { // -
140                         access_action("Editor/temporal-zoom-out");
141                 }
142                 if (b & CWIID_BTN_HOME) { // "home"
143                         // no op, yet. any suggestions?
144                         access_action("Editor/playhead-to-edit");
145                 }
146
147         }
148 }
149
150 void
151 WiimoteControlProtocol::update_led_state()
152 {
153         ENSURE_WIIMOTE_THREAD(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));
154
155         uint8_t state = 0;
156
157         if (session->transport_rolling()) {
158                 state |= CWIID_LED1_ON;
159         }
160
161         if (session->actively_recording()) {
162                 state |= CWIID_LED4_ON;
163         }
164
165         cwiid_set_led(wiimote_handle, state);
166 }
167
168 void
169 WiimoteControlProtocol::_wiimote_main ()
170 {
171         bdaddr_t bdaddr;
172         unsigned char rpt_mode = 0;
173
174         register_thread ("Wiimote");
175
176 wiimote_discovery:
177
178         std::cerr << "Wiimote: discovering, press 1+2" << std::endl;
179
180         while (!wiimote_handle && !main_thread_quit) {
181                 bdaddr = *BDADDR_ANY;
182                 callback_thread_registered_for_ardour = false;
183                 wiimote_handle = cwiid_open(&bdaddr, 0);
184
185                 if (!wiimote_handle && !main_thread_quit) {
186                         sleep(1); 
187                         // We don't know whether the issue was a timeout or a configuration 
188                         // issue
189                 }
190         }
191
192         if (main_thread_quit) {
193                 // The corner case where the wiimote is bound at the same time as
194                 // the control protocol is destroyed
195                 if (wiimote_handle) {
196                         cwiid_close(wiimote_handle);
197                 }
198                 wiimote_handle = 0;
199
200                 std::cerr << "Wiimote Control Protocol stopped before connected to a wiimote" << std::endl;
201                 return;
202         }
203
204         std::cerr << "Wiimote: connected" << std::endl;
205         WiimoteControlProtocol::button_state = 0;
206
207         if (cwiid_enable(wiimote_handle, CWIID_FLAG_REPEAT_BTN)) {
208                 std::cerr << "cwiid_enable(), error" << std::endl;
209                 cwiid_close(wiimote_handle);
210                 wiimote_handle = 0;
211                 return;
212         }
213         if (cwiid_set_mesg_callback(wiimote_handle, wiimote_control_protocol_cwiid_callback)) {
214                 std::cerr << "cwiid_set_mesg_callback(), couldn't connect callback" << std::endl;
215                 cwiid_close(wiimote_handle);
216                 wiimote_handle = 0;
217                 return;
218         } 
219         if (cwiid_command(wiimote_handle, CWIID_CMD_RPT_MODE, CWIID_RPT_BTN)) {
220                 std::cerr << "cwiid_command(), RPT_MODE error" << std::endl;
221                 cwiid_close(wiimote_handle);
222                 wiimote_handle = 0;
223                 return;
224         }
225
226         rpt_mode |= CWIID_RPT_BTN;
227         cwiid_enable(wiimote_handle, CWIID_FLAG_MESG_IFC);
228         cwiid_set_rpt_mode(wiimote_handle, rpt_mode);
229
230         transport_state_conn = session->TransportStateChange.connect(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));
231         record_state_conn = session->RecordStateChanged.connect(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));
232
233         std::cerr << "Wiimote: initialization done, waiting for callbacks / quit" << std::endl;
234
235         while (!main_thread_quit) {
236                 slot_mutex.lock();
237                 while (slot_list.empty() && !main_thread_quit && !restart_discovery)
238                         slot_cond.wait(slot_mutex);
239
240                 if (main_thread_quit) {
241                         slot_mutex.unlock();
242                         break;
243                 }
244
245                 if (restart_discovery) {
246                         std::cerr << "Wiimote: closing wiimote and restarting discovery" << std::endl;
247                         if (wiimote_handle) {
248                                 cwiid_close(wiimote_handle);
249                                 wiimote_handle = 0;
250                         }
251                         slot_mutex.unlock();
252                         restart_discovery = false;
253                         goto wiimote_discovery;
254                 }
255
256                 sigc::slot<void> call_me = *slot_list.begin();
257                 slot_list.pop_front();
258                 slot_mutex.unlock();
259
260                 call_me();
261         }
262
263
264         std::cerr << "Wiimote: main thread stopped" << std::endl;
265         return 0;
266 }
267
268
269 int
270 WiimoteControlProtocol::set_active (bool yn)
271 {
272         // Let's not care about this just yet
273         return 0;
274
275 }
276
277 XMLNode&
278 WiimoteControlProtocol::get_state()
279 {
280         XMLNode *node = new XMLNode ("Protocol");
281         node->add_property (X_("name"), _name);
282         node->add_property (X_("feedback"), "0");
283
284         return *node;
285 }
286
287 int
288 WiimoteControlProtocol::set_state(const XMLNode& node)
289 {
290         return 0;
291 }