1 /* FaderPort8 Button Interface
3 * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #ifndef _ardour_surfaces_fp8button_h_
21 #define _ardour_surfaces_fp8button_h_
25 #include "pbd/base_ui.h"
26 #include "pbd/signals.h"
30 namespace ArdourSurface {
32 /* virtual base-class and interface */
33 class FP8ButtonInterface
36 FP8ButtonInterface () {}
37 virtual ~FP8ButtonInterface () {}
40 PBD::Signal0<void> pressed;
41 PBD::Signal0<void> released;
43 virtual bool is_pressed () const { return false; }
44 virtual bool is_active () const { return false; }
46 virtual void ignore_release () {}
48 /* internal API - called from midi thread,
49 * user pressed/released button the device
51 virtual bool midi_event (bool) = 0;
53 /* internal API - called from surface thread
54 * set Light on the button
56 virtual void set_active (bool a) = 0;
57 virtual void set_color (uint32_t rgba) {}
58 virtual void set_blinking (bool) {}
60 static bool force_change; // used during init
63 /* ****************************************************************************
67 class FP8DummyButton : public FP8ButtonInterface
70 virtual void set_active (bool a) {}
71 virtual bool midi_event (bool) { return false; }
75 /* common implementation */
76 class FP8ButtonBase : public FP8ButtonInterface
79 FP8ButtonBase (FP8Base& b)
83 , _ignore_release (false)
88 bool is_pressed () const { return _pressed; }
89 bool is_active () const { return _active; }
91 virtual bool midi_event (bool a)
98 pressed (); /* EMIT SIGNAL */
100 if (_ignore_release) {
101 _ignore_release = false;
103 released (); /* EMIT SIGNAL */
109 void ignore_release () {
111 _ignore_release = true;
115 void set_blinking (bool yes) {
116 if (yes && !_blinking) {
118 _base.BlinkIt.connect_same_thread (_blink_connection, boost::bind (&FP8ButtonBase::blink, this, _1));
119 } else if (!yes && _blinking) {
120 _blink_connection.disconnect ();
130 bool _ignore_release;
132 virtual void blink (bool onoff) = 0;
135 PBD::ScopedConnection _blink_connection;
139 /* A basic LED or RGB button, not shift sensitive */
140 class FP8Button : public FP8ButtonBase
143 FP8Button (FP8Base& b, uint8_t id, bool color = false)
149 virtual void set_active (bool a)
151 if (_active == a && !force_change) {
155 _base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
158 void set_color (uint32_t rgba)
160 if (!_has_color || _rgba == rgba) {
164 _base.tx_midi3 (0x91, _midi_id, (_rgba >> 25) & 0x7f);
165 _base.tx_midi3 (0x92, _midi_id, (_rgba >> 17) & 0x7f);
166 _base.tx_midi3 (0x93, _midi_id, (_rgba >> 9) & 0x7f);
170 void blink (bool onoff)
172 if (!_active) { return; }
173 _base.tx_midi3 (0x90, _midi_id, onoff ? 0x7f : 0x00);
176 uint8_t _midi_id; // MIDI-note
180 /* footswitch and encoder-press buttons */
181 class FP8ReadOnlyButton : public FP8Button
184 FP8ReadOnlyButton (FP8Base& b, uint8_t id, bool color = false)
185 : FP8Button (b, id, color)
188 void set_active (bool) { }
191 /* virtual button. used for shift toggle. */
192 class ShadowButton : public FP8ButtonBase
195 ShadowButton (FP8Base& b)
199 PBD::Signal1<void, bool> ActiveChanged;
200 PBD::Signal0<void> ColourChanged;
202 uint32_t color () const { return _rgba; }
204 bool midi_event (bool a)
210 bool set_pressed (bool a)
212 return FP8ButtonBase::midi_event (a);
215 void set_active (bool a)
217 if (_active == a && !force_change) {
221 ActiveChanged (a); /* EMIT SIGNAL */
224 void set_color (uint32_t rgba)
234 void blink (bool onoff) {
235 if (!_active) { return; }
236 ActiveChanged (onoff);
240 /* Wraps 2 buttons with the same physical MIDI ID */
241 class FP8DualButton : public FP8ButtonInterface
244 FP8DualButton (FP8Base& b, uint8_t id, bool color = false)
253 _b0.ActiveChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::active_changed, this, false, _1));
254 _b1.ActiveChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::active_changed, this, true, _1));
256 _b0.ColourChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::colour_changed, this, false));
257 _b1.ColourChanged.connect_same_thread (_button_connections, boost::bind (&FP8DualButton::colour_changed, this, true));
261 bool midi_event (bool a) {
262 return (_shift ? _b1 : _b0).set_pressed (a);
265 void set_active (bool a) {
266 /* This button is never directly used
267 * by the libardour side API.
272 void active_changed (bool s, bool a) {
276 _base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
279 void colour_changed (bool s) {
280 if (s != _shift || !_has_color) {
283 uint32_t rgba = (_shift ? _b1 : _b0).color ();
288 _base.tx_midi3 (0x91, _midi_id, (rgba >> 25) & 0x7f);
289 _base.tx_midi3 (0x92, _midi_id, (rgba >> 17) & 0x7f);
290 _base.tx_midi3 (0x93, _midi_id, (rgba >> 9) & 0x7f);
293 FP8ButtonInterface* button () { return &_b0; }
294 FP8ButtonInterface* button_shift () { return &_b1; }
299 virtual void connect_toggle () = 0;
301 void shift_changed (bool shift) {
302 if (_shift == shift) {
305 (_shift ? _b1 : _b0).set_pressed (false);
307 active_changed (_shift, (_shift ? _b1 : _b0).is_active());
308 colour_changed (_shift);
314 uint8_t _midi_id; // MIDI-note
318 PBD::ScopedConnectionList _button_connections;
321 class FP8ShiftSensitiveButton : public FP8DualButton
324 FP8ShiftSensitiveButton (FP8Base& b, uint8_t id, bool color = false)
325 :FP8DualButton (b, id, color)
331 void connect_toggle ()
333 _base.ShiftButtonChange.connect_same_thread (_shift_connection, boost::bind (&FP8ShiftSensitiveButton::shift_changed, this, _1));
337 PBD::ScopedConnection _shift_connection;
340 class FP8ARMSensitiveButton : public FP8DualButton
343 FP8ARMSensitiveButton (FP8Base& b, uint8_t id, bool color = false)
344 :FP8DualButton (b, id, color)
350 void connect_toggle ()
352 _base.ARMButtonChange.connect_same_thread (_arm_connection, boost::bind (&FP8ARMSensitiveButton::shift_changed, this, _1));
356 PBD::ScopedConnection _arm_connection;
360 // short press: activate in press, deactivate on release,
361 // long press + hold, activate on press, de-activate directly on release
362 // e.g. mute/solo press + hold => changed()
363 class FP8MomentaryButton : public FP8ButtonInterface
366 FP8MomentaryButton (FP8Base& b, uint8_t id)
373 ~FP8MomentaryButton () {
374 _hold_connection.disconnect ();
377 PBD::Signal1<void, bool> StateChange;
379 void set_active (bool a)
381 if (_active == a && !force_change) {
385 _base.tx_midi3 (0x90, _midi_id, a ? 0x7f : 0x00);
390 _was_active_on_press = false;
391 _hold_connection.disconnect ();
394 bool midi_event (bool a)
403 _was_active_on_press = _active;
408 StateChange (true); /* EMIT SIGNAL */
409 Glib::RefPtr<Glib::TimeoutSource> hold_timer =
410 Glib::TimeoutSource::create (500);
411 hold_timer->attach (fp8_loop()->get_context());
412 _hold_connection = hold_timer->connect (sigc::mem_fun (*this, &FP8MomentaryButton::hold_timeout));
413 } else if (!a && _was_active_on_press) {
414 _hold_connection.disconnect ();
416 StateChange (false); /* EMIT SIGNAL */
417 } else if (!a && _momentaty) {
418 _hold_connection.disconnect ();
420 StateChange (false); /* EMIT SIGNAL */
427 uint8_t _midi_id; // MIDI-note
430 bool _was_active_on_press;
439 sigc::connection _hold_connection;
442 /* an auto-repeat button.
443 * press + hold emits continuous "press" events.
445 class FP8RepeatButton : public FP8Button
448 FP8RepeatButton (FP8Base& b, uint8_t id, bool color = false)
449 : FP8Button (b, id, color)
458 bool midi_event (bool a)
460 bool rv = FP8Button::midi_event (a);
469 _press_timeout_connection.disconnect ();
477 Glib::RefPtr<Glib::TimeoutSource> press_timer =
478 Glib::TimeoutSource::create (100);
479 press_timer->attach (fp8_loop()->get_context());
480 _press_timeout_connection = press_timer->connect (sigc::mem_fun (*this, &FP8RepeatButton::repeat_press));
497 sigc::connection _press_timeout_connection;
501 #endif /* _ardour_surfaces_fp8button_h_ */