Fast enough, Paul?
[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         std::cerr << "Wiimote: closed" << std::endl;
39 }
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         register_thread("Wiimote Discovery and Callback Thread");
174
175 wiimote_discovery:
176
177         std::cerr << "Wiimote: discovering, press 1+2" << std::endl;
178
179         while (!wiimote_handle && !main_thread_quit) {
180                 bdaddr = *BDADDR_ANY;
181                 callback_thread_registered_for_ardour = false;
182                 wiimote_handle = cwiid_open(&bdaddr, 0);
183
184                 if (!wiimote_handle && !main_thread_quit) {
185                         sleep(1); 
186                         // We don't know whether the issue was a timeout or a configuration 
187                         // issue
188                 }
189         }
190
191         if (main_thread_quit) {
192                 // The corner case where the wiimote is bound at the same time as
193                 // the control protocol is destroyed
194                 if (wiimote_handle) {
195                         cwiid_close(wiimote_handle);
196                 }
197                 wiimote_handle = 0;
198
199                 std::cerr << "Wiimote Control Protocol stopped before connected to a wiimote" << std::endl;
200                 return;
201         }
202
203         std::cerr << "Wiimote: connected" << std::endl;
204         WiimoteControlProtocol::button_state = 0;
205
206         if (cwiid_enable(wiimote_handle, CWIID_FLAG_REPEAT_BTN)) {
207                 std::cerr << "cwiid_enable(), error" << std::endl;
208                 cwiid_close(wiimote_handle);
209                 wiimote_handle = 0;
210                 return;
211         }
212         if (cwiid_set_mesg_callback(wiimote_handle, wiimote_control_protocol_cwiid_callback)) {
213                 std::cerr << "cwiid_set_mesg_callback(), couldn't connect callback" << std::endl;
214                 cwiid_close(wiimote_handle);
215                 wiimote_handle = 0;
216                 return;
217         } 
218         if (cwiid_command(wiimote_handle, CWIID_CMD_RPT_MODE, CWIID_RPT_BTN)) {
219                 std::cerr << "cwiid_command(), RPT_MODE error" << std::endl;
220                 cwiid_close(wiimote_handle);
221                 wiimote_handle = 0;
222                 return;
223         }
224
225         rpt_mode |= CWIID_RPT_BTN;
226         cwiid_enable(wiimote_handle, CWIID_FLAG_MESG_IFC);
227         cwiid_set_rpt_mode(wiimote_handle, rpt_mode);
228
229         transport_state_conn = session->TransportStateChange.connect(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));
230         record_state_conn = session->RecordStateChanged.connect(sigc::mem_fun(*this, &WiimoteControlProtocol::update_led_state));
231
232         std::cerr << "Wiimote: initialization done, waiting for callbacks / quit" << std::endl;
233
234         while (!main_thread_quit) {
235                 slot_mutex.lock();
236                 while (slot_list.empty() && !main_thread_quit && !restart_discovery)
237                         slot_cond.wait(slot_mutex);
238
239                 if (main_thread_quit) {
240                         slot_mutex.unlock();
241                         break;
242                 }
243
244                 if (restart_discovery) {
245                         std::cerr << "Wiimote: closing wiimote and restarting discovery" << std::endl;
246                         if (wiimote_handle) {
247                                 cwiid_close(wiimote_handle);
248                                 wiimote_handle = 0;
249                         }
250                         slot_mutex.unlock();
251                         restart_discovery = false;
252                         goto wiimote_discovery;
253                 }
254
255                 sigc::slot<void> call_me = *slot_list.begin();
256                 slot_list.pop_front();
257                 slot_mutex.unlock();
258
259                 call_me();
260         }
261
262
263         std::cerr << "Wiimote: main thread stopped" << std::endl;
264 }
265
266
267 int
268 WiimoteControlProtocol::set_active (bool yn)
269 {
270         // Let's not care about this just yet
271         return 0;
272
273 }
274
275 XMLNode&
276 WiimoteControlProtocol::get_state()
277 {
278         XMLNode *node = new XMLNode ("Protocol");
279         node->add_property (X_("name"), _name);
280         node->add_property (X_("feedback"), "0");
281
282         return *node;
283 }
284
285 int
286 WiimoteControlProtocol::set_state(const XMLNode& node)
287 {
288         return 0;
289 }