enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / surfaces / wiimote / wiimote.cc
1 /*
2     Copyright (C) 2009-2013 Paul Davis
3     Authors: Sampo Savolainen, Jannis Pohlmann
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <iostream>
22
23 #include "pbd/compose.h"
24 #include "pbd/error.h"
25 #include "ardour/debug.h"
26 #include "ardour/session.h"
27 #include "pbd/i18n.h"
28
29 #include "wiimote.h"
30
31 using namespace ARDOUR;
32 using namespace PBD;
33 using namespace std;
34
35 #include "pbd/abstract_ui.cc" // instantiate template
36
37 void wiimote_control_protocol_mesg_callback (cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], timespec *t);
38
39 WiimoteControlProtocol::WiimoteControlProtocol (Session& session)
40         : ControlProtocol (session, X_("Wiimote"))
41         , AbstractUI<WiimoteControlUIRequest> ("wiimote")
42         , wiimote (0)
43         , idle_source (0)
44         , button_state (0)
45         , callback_thread_registered (false)
46 {
47 }
48
49 WiimoteControlProtocol::~WiimoteControlProtocol ()
50 {
51         stop ();
52 }
53
54 bool
55 WiimoteControlProtocol::probe ()
56 {
57         return true;
58 }
59
60 int
61 WiimoteControlProtocol::set_active (bool yn)
62 {
63         int result;
64
65         DEBUG_TRACE (DEBUG::WiimoteControl, string_compose ("WiimoteControlProtocol::set_active init with yn: '%1'\n", yn));
66
67         /* do nothing if the active state is not changing */
68
69         if (yn == active()) {
70                 return 0;
71         }
72
73         if (yn) {
74                 /* activate Wiimote control surface */
75                 result = start ();
76         } else {
77                 /* deactivate Wiimote control surface */
78                 result = stop ();
79         }
80
81         ControlProtocol::set_active (yn);
82
83         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::set_active done\n");
84
85         return result;
86 }
87
88 XMLNode&
89 WiimoteControlProtocol::get_state ()
90 {
91         XMLNode& node (ControlProtocol::get_state());
92         node.add_property (X_("feedback"), "0");
93         return node;
94 }
95
96 int
97 WiimoteControlProtocol::set_state (const XMLNode&, int)
98 {
99         return 0;
100 }
101
102 void
103 WiimoteControlProtocol::do_request (WiimoteControlUIRequest* req)
104 {
105         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::do_request init\n");
106
107         if (req->type == CallSlot) {
108                 call_slot (MISSING_INVALIDATOR, req->the_slot);
109         } else if (req->type == Quit) {
110                 stop ();
111         }
112
113         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::do_request done\n");
114 }
115
116 int
117 WiimoteControlProtocol::start ()
118 {
119         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start init\n");
120
121         // update LEDs whenever the transport or recording state changes
122         session->TransportStateChange.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&WiimoteControlProtocol::update_led_state, this), this);
123         session->RecordStateChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&WiimoteControlProtocol::update_led_state, this), this);
124
125         // start the Wiimote control UI; it will run in its own thread context
126         BaseUI::run ();
127
128         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start done\n");
129
130         return 0;
131 }
132
133 int
134 WiimoteControlProtocol::stop ()
135 {
136         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop init\n");
137
138         // stop wiimote discovery, just in case
139         stop_wiimote_discovery ();
140
141         // close and reset the wiimote handle
142         if (wiimote) {
143                 cwiid_close (wiimote);
144                 wiimote = 0;
145                 callback_thread_registered = false;
146         }
147
148         // stop the Wiimote control UI
149         BaseUI::quit ();
150
151         // no longer update the LEDs
152         session_connections.drop_connections ();
153
154         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop done\n");
155
156         return 0;
157 }
158
159 void
160 WiimoteControlProtocol::thread_init ()
161 {
162         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::thread_init init\n");
163
164         pthread_set_name (X_("wiimote"));
165
166         // allow to make requests to the GUI and RT thread(s)
167         PBD::notify_event_loops_about_thread_creation (pthread_self (), X_("wiimote"), 2048);
168         BasicUI::register_thread ("wiimote");
169
170         // connect a Wiimote
171         start_wiimote_discovery ();
172
173         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::thread_init done\n");
174 }
175
176 void
177 WiimoteControlProtocol::start_wiimote_discovery ()
178 {
179         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start_wiimote_discovery init\n");
180
181         // connect to the Wiimote using an idle source
182         Glib::RefPtr<Glib::IdleSource> source = Glib::IdleSource::create ();
183         source->connect (sigc::mem_fun (*this, &WiimoteControlProtocol::connect_idle));
184         source->attach (_main_loop->get_context ());
185
186         // grab a reference on the underlying idle source to keep it around
187         idle_source = source->gobj ();
188         g_source_ref (idle_source);
189
190         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::start_wiimote_discovery done\n");
191 }
192
193 void
194 WiimoteControlProtocol::stop_wiimote_discovery ()
195 {
196         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop_wiimote_discovery init\n");
197
198         if (idle_source) {
199                 g_source_unref (idle_source);
200                 idle_source = 0;
201         }
202
203         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::stop_wiimote_discovery done\n");
204 }
205
206 bool
207 WiimoteControlProtocol::connect_idle ()
208 {
209         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::connect_idle init\n");
210
211         bool retry = false;
212
213         if (connect_wiimote ()) {
214                 stop_wiimote_discovery ();
215         }
216
217         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::connect_idle done\n");
218
219         return retry;
220 }
221
222 bool
223 WiimoteControlProtocol::connect_wiimote ()
224 {
225         // abort the discovery and do nothing else if we already have a Wiimote
226         if (wiimote) {
227                 return true;
228         }
229
230         bool success = false;
231
232         // if we don't have a Wiimote yet, try to discover it; if that
233         // fails, wait for a short period of time and try again
234         for (int i = 0; i < 5; ++i) {
235                 cerr << "Wiimote: Not discovered yet, press 1+2 to connect" << endl;
236
237                 bdaddr_t bdaddr = {{ 0, 0, 0, 0, 0, 0 }};
238                 wiimote = cwiid_open (&bdaddr, 0);
239                 callback_thread_registered = false;
240                 if (wiimote) {
241                         // a Wiimote was discovered
242                         cerr << "Wiimote: Connected successfully" << endl;
243
244                         // attach the WiimoteControlProtocol object to the Wiimote handle
245                         if (cwiid_set_data (wiimote, this)) {
246                                 cerr << "Wiimote: Failed to attach control protocol" << endl;
247                         } else {
248                                 success = true;
249                                 // clear the last button state to start processing events cleanly
250                                 button_state = 0;
251                                 break;
252                         }
253                 }
254         }
255
256         // enable message based communication with the Wiimote
257         if (success && cwiid_enable (wiimote, CWIID_FLAG_MESG_IFC)) {
258                 cerr << "Wiimote: Failed to enable message based communication" << endl;
259                 success = false;
260         }
261
262         // enable button events to be received from the Wiimote
263         if (success && cwiid_command (wiimote, CWIID_CMD_RPT_MODE, CWIID_RPT_BTN)) {
264                 cerr << "Wiimote: Failed to enable button events" << endl;
265                 success = false;
266         }
267
268         // receive an event for every single button pressed, not just when
269         // a different button was pressed than before
270         if (success && cwiid_enable (wiimote, CWIID_FLAG_REPEAT_BTN)) {
271                 cerr << "Wiimote: Failed to enable repeated button events" << endl;
272                 success = false;
273         }
274
275         // be notified of new input events
276         if (success && cwiid_set_mesg_callback (wiimote, wiimote_control_protocol_mesg_callback)) {
277         }
278
279         // reset Wiimote handle if the configuration failed
280         if (!success && wiimote) {
281                 cwiid_close (wiimote);
282                 wiimote = 0;
283                 callback_thread_registered = false;
284         }
285
286         return success;
287 }
288
289 void
290 WiimoteControlProtocol::update_led_state ()
291 {
292         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state init\n");
293
294         uint8_t state = 0;
295
296         // do nothing if we do not have a Wiimote
297         if (!wiimote) {
298                 DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state no wiimote connected\n");
299                 return;
300         }
301
302         // enable LED1 if Ardour is playing
303         if (session->transport_rolling ()) {
304                 DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state playing, activate LED1\n");
305                 state |= CWIID_LED1_ON;
306         }
307
308         // enable LED4 if Ardour is recording
309         if (session->actively_recording ()) {
310                 DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state recording, activate LED4\n");
311                 state |= CWIID_LED4_ON;
312         }
313
314         // apply the LED state
315         cwiid_set_led (wiimote, state);
316
317         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::update_led_state done\n");
318 }
319
320 void
321 WiimoteControlProtocol::wiimote_callback (int mesg_count, union cwiid_mesg mesg[])
322 {
323         // register the cwiid callback thread if that hasn't happened yet
324         if (!callback_thread_registered) {
325                 BasicUI::register_thread ("wiimote callback");
326                 callback_thread_registered = true;
327         }
328
329         for (int i = 0; i < mesg_count; i++) {
330                 // restart Wiimote discovery when receiving errors
331                 if (mesg[i].type == CWIID_MESG_ERROR) {
332                         cerr << "Wiimote: disconnected" << endl;
333                         cwiid_close (wiimote);
334                         wiimote = 0;
335                         callback_thread_registered = false;
336                         start_wiimote_discovery ();
337                         return;
338                 }
339
340                 // skip non-button events
341                 if (mesg[i].type != CWIID_MESG_BTN) {
342                         continue;
343                 }
344
345                 // drop buttons from the event that were already pressed before
346                 uint16_t b = mesg[i].btn_mesg.buttons & ~button_state;
347
348                 // remember new button state
349                 button_state = mesg[i].btn_mesg.buttons;
350
351                 if (button_state & CWIID_BTN_B) {
352                         // B + A = abort recording and jump back
353                         if (b & CWIID_BTN_A) {
354                                 access_action ("Transport/ToggleRollForgetCapture");
355                         }
356
357                         // B + left = move playhead to previous region boundary
358                         if (b & CWIID_BTN_LEFT) {
359                                 access_action ("Editor/playhead-to-previous-region-boundary");
360                         }
361
362                         // B + right = move playhead to next region boundary
363                         if (b & CWIID_BTN_RIGHT) {
364                                 access_action ("Editor/playhead-to-next-region-boundary");
365                         }
366
367                         // B + up = move playhead to next marker
368                         if (b & CWIID_BTN_UP) {
369                                 next_marker ();
370                         }
371
372                         // B + down = move playhead to prev marker
373                         if (b & CWIID_BTN_DOWN) {
374                                 prev_marker ();
375                         }
376
377                         // B + Home = add marker at playhead
378                         if (b & CWIID_BTN_HOME) {
379                                 access_action ("Editor/add-location-from-playhead");
380                         }
381
382                         // B + minus = move playhead to the start
383                         if (b & CWIID_BTN_MINUS) {
384                                 access_action ("Transport/GotoStart");
385                         }
386
387                         // B + plus = move playhead to the end
388                         if (b & CWIID_BTN_PLUS) {
389                                 access_action ("Transport/GotoEnd");
390                         }
391                 } else {
392                         // A = toggle playback
393                         if (b & CWIID_BTN_A) {
394                                 access_action ("Transport/ToggleRoll");
395                         }
396
397                         // 1 = toggle recording on the current track
398                         if (b & CWIID_BTN_1) {
399                                 access_action ("Editor/track-record-enable-toggle");
400                         }
401
402                         // 2 = enable recording in general
403                         if (b & CWIID_BTN_2) {
404                                 rec_enable_toggle ();
405                         }
406
407                         // left = move playhead back a bit
408                         if (b & CWIID_BTN_LEFT) {
409                                 access_action ("Editor/nudge-playhead-backward");
410                         }
411
412                         // right = move playhead forward a bit
413                         if (b & CWIID_BTN_RIGHT) {
414                                 access_action ("Editor/nudge-playhead-forward");
415                         }
416
417                         // up = select previous track
418                         if (b & CWIID_BTN_UP) {
419                                 access_action ("Editor/select-prev-route");
420                         }
421
422                         // down = select next track
423                         if (b & CWIID_BTN_DOWN) {
424                                 access_action ("Editor/select-next-route");
425                         }
426
427                         // + = zoom in
428                         if (b & CWIID_BTN_PLUS) {
429                                 access_action ("Editor/temporal-zoom-in");
430                         }
431
432                         // - = zoom out
433                         if (b & CWIID_BTN_MINUS) {
434                                 access_action ("Editor/temporal-zoom-out");
435                         }
436
437                         // home = no-op
438                         if (b & CWIID_BTN_HOME) {
439                                 access_action ("Editor/playhead-to-edit");
440                         }
441                 }
442         }
443 }
444
445 void
446 wiimote_control_protocol_mesg_callback (cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], timespec *)
447 {
448         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::mesg_callback init\n");
449
450         WiimoteControlProtocol *protocol = reinterpret_cast<WiimoteControlProtocol*> (const_cast<void*>(cwiid_get_data (wiimote)));
451
452         if (protocol) {
453                 protocol->wiimote_callback (mesg_count, mesg);
454         }
455
456         DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::mesg_callback done\n");
457 }
458
459
460 void*
461 WiimoteControlProtocol::request_factory (uint32_t num_requests)
462 {
463         /* AbstractUI<T>::request_buffer_factory() is a template method only
464            instantiated in this source module. To provide something visible for
465            use in the interface/descriptor, we have this static method that is
466            template-free.
467         */
468         return request_buffer_factory (num_requests);
469 }