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