Skeleton for NI Maschine2 Surface
[ardour.git] / libs / surfaces / maschine2 / m2_button.h
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
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, or (at your option)
7  * 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 Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #ifndef _ardour_surfaces_m2button_h_
20 #define _ardour_surfaces_m2button_h_
21
22 #include <stdint.h>
23 #include "gtkmm2ext/colors.h"
24 #include "pbd/signals.h"
25
26 namespace ArdourSurface {
27
28 class M2ButtonInterface
29 {
30         public:
31                 M2ButtonInterface () {}
32                 virtual ~M2ButtonInterface () {}
33
34                 /* user API */
35                 PBD::Signal1<void, bool> changed;
36                 PBD::Signal0<void> pressed;
37                 PBD::Signal0<void> released;
38
39                 virtual void set_blinking (bool) {}
40                 virtual void set_color (uint32_t rgba) {}
41
42                 virtual bool is_pressed () const { return false; }
43                 virtual bool active () const { return is_pressed (); }
44
45                 virtual void ignore_release () {}
46
47                 // TODO allow to suspend *next* release signal
48                 // e.g. press + hold "grid", move encoder -> release "grid" -> noop
49
50                 /* internal API - called from device thread */
51                 virtual bool set_active (bool a) { return false; }
52                 virtual uint8_t lightness (float) const { return 0; }
53                 virtual uint32_t color (float) const { return 0; }
54 };
55
56 class M2Button : public M2ButtonInterface
57 {
58         public:
59                 M2Button ()
60                         : M2ButtonInterface ()
61                         , _pressed (false)
62                         , _blink (false)
63                         , _ignore_release (false)
64                         , _lightness (0)
65                         , _rgba (0)
66                 {}
67
68                 /* user API */
69                 void set_blinking (bool en) {
70                         _blink = en;
71                 }
72
73                 virtual void set_color (uint32_t rgba) {
74                         _rgba = rgba;
75                         /* 7 bit color */
76                         const uint8_t r = ((rgba >> 24) & 0xff) >> 1;
77                         const uint8_t g = ((rgba >> 16) & 0xff) >> 1;
78                         const uint8_t b = ((rgba >>  8) & 0xff) >> 1;
79                         _lightness = std::max (r, std::max (g, b));
80                 }
81
82                 bool is_pressed () const { return _pressed; }
83
84                 void ignore_release () {
85                         if (_pressed) {
86                                 _ignore_release = true;
87                         }
88                 }
89                 
90                 /* internal API - called from device thread */
91                 virtual bool set_active (bool a) {
92                         if (a == _pressed) {
93                                 return false;
94                         }
95                         _pressed = a;
96
97                         if (a) {
98                                 pressed (); /* EMIT SIGNAL */
99                         } else {
100                                 if (_ignore_release) {
101                                         _ignore_release = false;
102                                 } else {
103                                         released (); /* EMIT SIGNAL */
104                                 }
105                         }
106                         changed (a); /* EMIT SIGNAL */
107                         return true;
108                 }
109
110                 uint8_t lightness (float blink) const {
111                         if (_blink && blink >= 0.f && blink <= 1.f) {
112                                 return (uint8_t) floorf(blink * _lightness);
113                         }
114                         return _lightness;
115                 }
116
117                 uint32_t color (float blink) const {
118                         if (_blink && blink >= 0.f && blink <= 1.f) {
119                                 Gtkmm2ext::HSV hsv (_rgba);
120                                 Gtkmm2ext::HSV s (hsv.shade (blink));
121                                 return s.color();
122                         }
123                         return _rgba;
124                 }
125
126         protected:
127                 bool     _pressed;
128                 bool     _blink;
129                 bool     _ignore_release;
130                 uint8_t  _lightness;
131                 uint32_t _rgba;
132 };
133
134 class M2StatelessButton : public M2Button
135 {
136         public:
137                 M2StatelessButton () : M2Button () {}
138
139                 bool set_active (bool a) {
140                         if (a == _pressed) {
141                                 return false;
142                         }
143                         if (a) {
144                                 set_color (0xffffffff);
145                         } else {
146                                 set_color (0x000000ff);
147                         }
148                         return M2Button::set_active (a);
149                 }
150 };
151
152 class M2ToggleButton : public M2Button
153 {
154         public:
155                 M2ToggleButton ()
156                 : M2Button ()
157                 , _active (false)
158                 {
159                         changed.connect_same_thread (changed_connection, boost::bind (&M2ToggleButton::change_event, this, _1));
160                 }
161
162                 PBD::Signal1<void, bool> toggled;
163                 bool active () const { return _active; }
164
165         protected:
166                 void change_event (bool down) {
167                         if (down) { return; }
168                         _active = !_active;
169                         set_color (_active ? 0xffffffff : 0x000000ff);
170                         toggled (_active);
171                 }
172
173                 PBD::ScopedConnection changed_connection;
174                 bool _active;
175 };
176
177 class M2ToggleHoldButton : public M2Button
178 {
179         public:
180                 M2ToggleHoldButton ()
181                 : M2Button ()
182                 , _active (false)
183                 , _active_on_release (false)
184                 {
185                         changed.connect_same_thread (changed_connection, boost::bind (&M2ToggleHoldButton::change_event, this, _1));
186                 }
187
188                 PBD::Signal1<void, bool> toggled;
189                 bool active () const { return _active; }
190                 void unset_active_on_release () { if (is_pressed ()) { _active_on_release = false; } }
191
192         protected:
193                 void change_event (bool down) {
194                         if (down) {
195                                 if (_active) {
196                                         _active_on_release = false;
197                                         return;
198                                 }
199                                 _active = true;
200                                 _active_on_release = true;
201                         } else {
202                                 if (_active == _active_on_release) {
203                                         return;
204                                 }
205                                 _active = _active_on_release;
206                         }
207
208                         set_color (_active ? 0xffffffff : 0x000000ff);
209                         toggled (_active);
210                 }
211
212                 PBD::ScopedConnection changed_connection;
213                 bool _active;
214                 bool _active_on_release;
215 };
216
217
218 } /* namespace */
219 #endif /* _ardour_surfaces_m2button_h_ */
220