change several startup messages to be triggered only by -D (debug) options
[ardour.git] / libs / surfaces / powermate / powermate.cc
1 /*
2         powermate.cc
3         Ben Loftis
4         Created: 03/26/07 20:07:56
5 */
6
7
8 #include <linux/input.h>
9 #include <cstring>
10 #include <cerrno>
11 #include <cstdio>
12 #include <unistd.h>
13 #include <fcntl.h>
14
15 #include <glibmm.h>
16
17 #include "pbd/pthread_utils.h"
18 #include "pbd/xml++.h"
19 #include "pbd/error.h"
20
21 #include "ardour/debug.h"
22
23 #include "powermate.h"
24 #include "i18n.h"
25
26 using namespace ARDOUR;
27 using namespace std;
28 using namespace sigc;
29 using namespace PBD;
30
31 #define NUM_VALID_PREFIXES 2
32
33 static const char *valid_prefix[NUM_VALID_PREFIXES] = {
34   "Griffin PowerMate",
35   "Griffin SoundKnob"
36 };
37
38 #define NUM_EVENT_DEVICES 16
39
40 int open_powermate (const char *dev, int mode)
41 {
42         if (!Glib::file_test (dev, Glib::FILE_TEST_EXISTS)) {
43                 return -1;
44         }
45         
46         int fd = open(dev, mode);
47         int i;
48         char name[255];
49         
50         if (fd < 0) {
51                 if (errno != EACCES) {
52                         error << string_compose ("Unable to open \"%1\": %2", dev, strerror(errno)) << endmsg;
53                 }
54                 return -1;
55         }
56
57         /* placate valgrind */
58         name[0] = '\0';
59
60         if (ioctl (fd, EVIOCGNAME (sizeof(name)), name) < 0) {
61                 error << string_compose ("\"%1\": EVIOCGNAME failed: %2", dev, strerror(errno)) << endmsg;
62                 close (fd);
63                 return -1;
64         }
65
66         // it's the correct device if the prefix matches what we expect it to be:
67         for (i = 0; i < NUM_VALID_PREFIXES; ++i) {
68                 if (!strncasecmp (name, valid_prefix[i], strlen (valid_prefix[i]))) {
69                         return fd;
70                 }
71         }
72         
73         close (fd);
74         return -1;
75 }
76
77 int find_powermate(int mode)
78 {
79         char devname[256];
80         int i, r;
81         
82         for (i = 0; i < NUM_EVENT_DEVICES; i++) {
83                 sprintf (devname, "/dev/input/event%d", i);
84                 r = open_powermate (devname, mode);
85                 if (r >= 0) {
86                         return r;
87                 }
88         }
89         
90         return -1;
91 }
92
93 PowermateControlProtocol::PowermateControlProtocol (Session& s)
94         : ControlProtocol  (s, "powermate", 0 /* XXX need an event loop here */)
95 {
96 }
97
98 PowermateControlProtocol::~PowermateControlProtocol ()
99 {
100         set_active (false);
101 }
102
103 bool
104 PowermateControlProtocol::probe ()
105 {
106         int port = find_powermate( O_RDONLY ); 
107
108         if (port < 0) {
109                 if (errno == ENOENT) {
110                         DEBUG_TRACE (DEBUG::ControlProtocols, "Powermate device not found; perhaps you have no powermate connected");
111                 } else {
112                         DEBUG_TRACE (DEBUG::ControlProtocols, string_compose ("powermate: Opening of powermate failed - %1\n", strerror(errno)));
113                 }
114                 return false;
115         }
116
117         close (port);
118         return true;
119 }
120
121 int
122 PowermateControlProtocol::set_active (bool inActivate)
123 {
124         if (inActivate != _active) {
125
126                 if (inActivate) {
127
128                         mPort = find_powermate(O_RDONLY);
129                         
130                         if ( mPort < 0 ) {
131                                 return -1;
132                         }
133                         
134                         if (pthread_create_and_store ("Powermate", &mThread, SerialThreadEntry, this) == 0) {
135                                 _active = true;
136                         } else {
137                                 return -1;
138                         }
139
140                         printf("Powermate Control Protocol activated\n");
141
142                 } else {
143                         pthread_cancel (mThread);
144                         close (mPort);
145                         _active = false;
146                         printf("Powermate Control Protocol deactivated\n");
147                 } 
148         }
149
150         return 0;
151 }
152
153 XMLNode&
154 PowermateControlProtocol::get_state () 
155 {
156         XMLNode* node = new XMLNode (X_("Protocol"));
157         node->add_property (X_("name"), _name);
158         return *node;
159 }
160
161 int
162 PowermateControlProtocol::set_state (const XMLNode& /*node*/, int /*version*/)
163 {
164         return 0;
165 }
166
167
168 void*
169 PowermateControlProtocol::SerialThreadEntry (void* arg)
170 {
171         static_cast<PowermateControlProtocol*>(arg)->register_thread ("Powermate");
172         return static_cast<PowermateControlProtocol*>(arg)->SerialThread ();
173 }
174
175 #define BUFFER_SIZE 32
176
177 bool held = false;
178 bool skippingMarkers = false;
179
180 void
181 PowermateControlProtocol::ProcessEvent(struct input_event *ev)
182 {
183 #ifdef VERBOSE
184   fprintf(stderr, "type=0x%04x, code=0x%04x, value=%d\n",
185           ev->type, ev->code, (int)ev->value);
186 #endif
187
188   switch(ev->type){
189   case EV_MSC:
190     printf("The LED pulse settings were changed; code=0x%04x, value=0x%08x\n", ev->code, ev->value);
191     break;
192   case EV_REL:
193     if(ev->code != REL_DIAL)
194       fprintf(stderr, "Warning: unexpected rotation event; ev->code = 0x%04x\n", ev->code);
195     else{
196         if (held) {
197                 //click and hold to skip forward and back by markers
198                 skippingMarkers = true;;
199                 if (ev->value > 0)
200                         next_marker();
201                 else
202                         prev_marker();
203         } else {
204                 //scale the range so that we can go from +/-8x within 180 degrees, with less precision at the higher speeds 
205                 float speed = get_transport_speed();
206                 speed += (float)ev->value * 0.05;
207                 if (speed > 1.5 || speed < -1.5 )
208                         speed += ev->value;
209                 set_transport_speed( speed );
210         }
211     }
212     break;
213   case EV_KEY:
214     if(ev->code != BTN_0)
215       fprintf(stderr, "Warning: unexpected key event; ev->code = 0x%04x\n", ev->code);
216     else
217       if (ev->value)
218                 held = true;
219       else {
220                 held = false;
221                 if (skippingMarkers) {
222                         skippingMarkers = false;
223                 } else {
224                         if (get_transport_speed() == 0.0) {
225                                 set_transport_speed(1.0);
226                         } else {
227                                 set_transport_speed(0.0);
228                         }
229                 }
230         }
231     break;
232   }
233
234   fflush(stdout);
235 }
236
237 void*
238 PowermateControlProtocol::SerialThread ()
239 {
240   struct input_event ibuffer[BUFFER_SIZE];
241   int r, events, i;
242
243   while(1){
244     r = read(mPort, ibuffer, sizeof(struct input_event) * BUFFER_SIZE);
245     if( r > 0 ){
246                 events = r / sizeof(struct input_event);
247       for(i=0; i<events; i++)
248                 ProcessEvent(&ibuffer[i]);
249     }else{
250       fprintf(stderr, "read() failed: %s\n", strerror(errno));
251       return (void*) 0;
252     }
253   }
254
255         return (void*) 0;
256 }
257
258