1 /*
2 * mediactrl.c -- Jog Shuttle device support
3 * Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
4 * Taken from Kino 1.1.1, with permission.
5 * Modified by Chris MacGregor <chris-avidemux@bouncingdog.com> to change the
6 * shuttle ring value range to -1.0 to +1.0, among other things.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22 #include "config.h"
23
24 #ifdef USE_JOG
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
31 #include <asm/types.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <linux/input.h>
38 #include <linux/version.h>
39
40 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
41 struct input_id {
42 __u16 bustype;
43 __u16 vendor;
44 __u16 product;
45 __u16 version;
46 };
47 #endif
48
49 #include "mediactrl.h"
50
51 static char *_shuttle_name = "Shuttle";
52 static char *_jog_name = "Jog";
53
54 /* Dan D. had a funny button numbering scheme that I think will just confuse
55 * people, so I've changed the tables below to what I believe will make the
56 * buttons come out the way people will expect. I only have a Contour Shuttle
57 * Pro to test with, though.
58 * P.S. I didn't touch the X-Keys table because it was too confusing.
59 * -- Chris MacGregor <chris-avidemux@bouncingdog.com>
60 */
61
62 /*
63 ShuttlePro keys
64 */
65 static struct media_ctrl_key mc_shuttle_pro_keys[] =
66 {
67 { 0x100, "Button 1", MEDIA_CTRL_F1 },
68 { 0x101, "Button 2", MEDIA_CTRL_F2 },
69 { 0x102, "Button 3", MEDIA_CTRL_F3 },
70 { 0x103, "Button 4", MEDIA_CTRL_F4 },
71 { 0x104, "Button 5", MEDIA_CTRL_B1 },
72 { 0x105, "Button 6", MEDIA_CTRL_B2 },
73 { 0x106, "Button 7", MEDIA_CTRL_B3 },
74 { 0x107, "Button 8", MEDIA_CTRL_B4 },
75 { 0x108, "Button 9", MEDIA_CTRL_B5},
76 { 0x109, "Button 10", MEDIA_CTRL_B6 },
77 { 0x10a, "Button 11", MEDIA_CTRL_B7 },
78 { 0x10b, "Button 12", MEDIA_CTRL_B8 },
79 { 0x10c, "Button 13", MEDIA_CTRL_B9 },
80 { 0, NULL, 0 }
81 };
82
83
84 /*
85 ShuttleXPress keys
86 */
87 static struct media_ctrl_key mc_shuttle_xpress_keys[] =
88 {
89 { 0x104, "Button B4", MEDIA_CTRL_B1 },
90 { 0x105, "Button B2", MEDIA_CTRL_B2 },
91 { 0x106, "Button B1", MEDIA_CTRL_B3 },
92 { 0x107, "Button B3", MEDIA_CTRL_B4 },
93 { 0x108, "Button B5", MEDIA_CTRL_B5 },
94 { 0, NULL, 0 }
95 };
96
97
98 /*
99 JLCooper MCS3 Keys
100 */
101 static struct media_ctrl_key mc_jlcooper_mcs3_keys[] =
102 {
103 { 0x107, "F1", MEDIA_CTRL_F1 },
104 { 0x101, "F2", MEDIA_CTRL_F2 },
105 { 0x105, "F3", MEDIA_CTRL_F3 },
106 { 0x102, "F4", MEDIA_CTRL_F4 },
107 { 0x103, "F5", MEDIA_CTRL_F5 },
108 { 0x104, "F6", MEDIA_CTRL_F6 },
109 { 0x10d, "W1", MEDIA_CTRL_B1 },
110 { 0x10e, "W2", MEDIA_CTRL_B2 },
111 { 0x100, "W3", MEDIA_CTRL_B3 },
112 { 0x106, "W4", MEDIA_CTRL_B4 },
113 { 0x110, "W5", MEDIA_CTRL_B5 },
114 { 0x111, "W6", MEDIA_CTRL_B6 },
115 { 0x115, "W7", MEDIA_CTRL_B7 },
116 { 0x116, "STICK_LEFT", MEDIA_CTRL_STICK_LEFT },
117 { 0x113, "STICK_RIGHT", MEDIA_CTRL_STICK_RIGHT },
118 { 0x114, "STICK_UP", MEDIA_CTRL_STICK_UP },
119 { 0x112, "STICK_DOWN", MEDIA_CTRL_STICK_DOWN },
120 { 0x10f, "Rewind", MEDIA_CTRL_REWIND },
121 { 0x108, "Fast Forward", MEDIA_CTRL_FAST_FORWARD },
122 { 0x109, "Stop", MEDIA_CTRL_STOP },
123 { 0x10a, "Play", MEDIA_CTRL_PLAY },
124 { 0x10b, "Record", MEDIA_CTRL_RECORD },
125 { 0, NULL, 0 }
126 };
127
128
129 /*
130 Griffin PowerMate
131 */
132 static struct media_ctrl_key mc_powermate_keys[] =
133 {
134 { BTN_0, "Button", MEDIA_CTRL_B1 },
135 { 0, NULL, 0 }
136 };
137
138
139 /*
140 X-Keys Jog/Shuttle
141 */
142 static struct media_ctrl_key mc_x_keys[] =
143 {
144 { 0x102, "Button L1", MEDIA_CTRL_F1 },
145 { 0x103, "Button L2", MEDIA_CTRL_F9 },
146 { 0x104, "Button L3", MEDIA_CTRL_B1 },
147 { 0x105, "Button L4", MEDIA_CTRL_B3 },
148 { 0x106, "Button L5", MEDIA_CTRL_B5 },
149 { 0x10a, "Button L6", MEDIA_CTRL_F2 },
150 { 0x10b, "Button L7", MEDIA_CTRL_F10 },
151 { 0x10c, "Button L8", MEDIA_CTRL_B2 },
152 { 0x10d, "Button L9", MEDIA_CTRL_B4 },
153 { 0x10e, "Button L10", MEDIA_CTRL_B6 },
154 { 0x112, "Button C1", MEDIA_CTRL_F3 },
155 { 0x11a, "Button C2", MEDIA_CTRL_F4 },
156 { 0x122, "Button C3", MEDIA_CTRL_F5 },
157 { 0x12a, "Button C4", MEDIA_CTRL_F6 },
158 { 0x113, "Button C5", MEDIA_CTRL_F11 },
159 { 0x11b, "Button C6", MEDIA_CTRL_F12 },
160 { 0x123, "Button C7", MEDIA_CTRL_F13 },
161 { 0x12b, "Button C8", MEDIA_CTRL_F14 },
162 { 0x132, "Button R1", MEDIA_CTRL_F7 },
163 { 0x133, "Button R2", MEDIA_CTRL_F15 },
164 { 0x134, "Button R3", MEDIA_CTRL_B7 },
165 { 0x135, "Button R4", MEDIA_CTRL_B9 },
166 { 0x136, "Button R5", MEDIA_CTRL_B11 },
167 { 0x13a, "Button R6", MEDIA_CTRL_F8 },
168 { 0x13b, "Button R7", MEDIA_CTRL_F16 },
169 { 0x13c, "Button R8", MEDIA_CTRL_B8 },
170 { 0x13d, "Button R9", MEDIA_CTRL_B10 },
171 { 0x13e, "Button R10", MEDIA_CTRL_B12 },
172 { 0, NULL, 0 }
173 };
174
media_ctrl_get_key(struct media_ctrl * ctrl,int code,int * index)175 struct media_ctrl_key *media_ctrl_get_key(struct media_ctrl *ctrl, int code, int *index)
176 {
177 int i = 0;
178 struct media_ctrl_key *keys = ctrl->device->keys;
179
180 while ( keys[i].key != 0 ) {
181 if (keys[i].key == code) {
182 if (index != NULL)
183 *index = i;
184 return &keys[i];
185 }
186 i++;
187 }
188
189 return NULL;
190 }
191
192
translate_contour_hid_event(struct media_ctrl * ctrl,struct input_event * ev,struct media_ctrl_event * me)193 void translate_contour_hid_event(struct media_ctrl *ctrl, struct input_event *ev,
194 struct media_ctrl_event *me)
195 {
196
197 int lv, cv;
198
199 me->type = MEDIA_CTRL_EVENT_NONE;
200
201 if (ev->type == EV_REL) {
202 /* First check the outer dial */
203 if (ev->code == REL_WHEEL) {
204
205 cv = (signed int)ev->value;
206 // I don't know why the following line is there, but
207 // it makes the wheel less responsive. If it's too
208 // touchy, I think it'd be better to reduce the
209 // absolute value of cv by 1 instead (so we don't go
210 // straight from 0 to -2 or +2, which is what happens
211 // with the line below).
212 // -- Chris MacGregor <chris-avidemux@bouncingdog.com>
213 //if (cv == 1 || cv == -1 ) cv = 0;
214 // Aha, now I understand: the silly Contour ShuttlePro
215 // (at least, and perhaps its brethren) never returns
216 // a 0 when the shuttle ring is returned to its home
217 // position - you can only get -7 to -1 and 1 to 7 out
218 // of it, so if you DON'T ignore -1 and 1, you will
219 // always appear to be in motion. :-( I don't know
220 // how they figured it was smart to not return 0 for
221 // the home position...Oh, well, back to ignoring -1
222 // and 1. But I'm going to do it in a way that leaves
223 // the remaining values proportionate.
224
225 //printf("Shuttle raw: %d\n", cv);
226 if ( cv == ctrl->lastshu ) return;
227 ctrl->lastshu = cv;
228
229 //printf("Shuttle: %d\n", cv);
230 me->type = MEDIA_CTRL_EVENT_SHUTTLE;
231 if (cv < 0)
232 cv += 1;
233 else if (cv > 0)
234 cv -= 1;
235
236 me->fvalue = cv / 6.0;
237 me->value = me->fvalue * 15;
238 me->name = _shuttle_name;
239
240 } else if (ev->code == REL_DIAL) {
241
242 if ( ctrl->lastval == -1 ) ctrl->lastval = ev->value;
243 lv = ctrl->lastval;
244 cv = ev->value;
245
246 if ( lv == cv ) return;
247
248 ctrl->lastval = cv;
249
250 if (cv < 10 && lv > 0xF0) cv +=0x100;
251 if (lv < 10 && cv > 0xF0) lv +=0x100;
252
253 me->type = MEDIA_CTRL_EVENT_JOG;
254 me->value = cv-lv;
255 me->name = _jog_name;
256
257 ctrl->jogpos += me->value;
258 //printf("Jog: %06ld (%d)\n", ctrl->jogpos, me->value);
259 }
260 return;
261 } else if (ev->type == EV_KEY) {
262 int index;
263 struct media_ctrl_key *key = media_ctrl_get_key(ctrl, ev->code, &index);
264 if ( key == NULL ) return;
265
266 me->type = MEDIA_CTRL_EVENT_KEY;
267 me->code = key->code;
268 me->value = ev->value;
269 me->name = ( char* )key->name;
270 me->index = index;
271
272 //printf("Key: %04x %02x: %s\n", ev->code, ev->value, key->name);
273
274 }
275
276 }
277
translate_compliant(struct media_ctrl * ctrl,struct input_event * ev,struct media_ctrl_event * me)278 void translate_compliant(struct media_ctrl *ctrl, struct input_event *ev, struct media_ctrl_event *me)
279 {
280 me->type = 0;
281
282 // printf("Translate %02x %02x\n", ev->type, ev->code );
283
284 if (ev->type == EV_REL) {
285 if (ev->code == REL_DIAL) {
286
287 me->type = MEDIA_CTRL_EVENT_JOG;
288 me->value = (signed int)ev->value;
289 me->name = _jog_name;
290
291 ctrl->jogpos += me->value;
292 //printf("Jog: %06ld (%d)\n", ctrl->jogpos, me->value);
293 }
294 return;
295 } else if (ev->type == EV_ABS) {
296 // printf("ABS\n" );
297 if ( ev->code == 0x1c || ev->code == ABS_THROTTLE ) {
298 //printf("ABS_MISC\n" );
299 me->type = MEDIA_CTRL_EVENT_SHUTTLE;
300 me->value = (signed int)ev->value;
301 me->fvalue = me->value / 15.0;
302 me->name = _shuttle_name;
303
304 ctrl->shuttlepos = me->value;
305 //printf("Shuttle: %06d (%d)\n", ctrl->shuttlepos, me->value);
306 }
307 } else if (ev->type == EV_KEY) {
308 int index;
309 struct media_ctrl_key *key = media_ctrl_get_key(ctrl, ev->code, &index);
310 if ( key == NULL ) return;
311
312 me->type = MEDIA_CTRL_EVENT_KEY;
313 me->code = key->code;
314 me->value = ev->value;
315 me->name = ( char* )key->name;
316 me->index = index;
317
318 //printf("Key: %04x %02x: %s\n", ev->code, ev->value, key->name);
319
320 }
321 }
322
323 struct media_ctrl_device supported_devices[] = {
324 { 0x0b33, 0x0030, "Contour ShuttlePRO v2", mc_shuttle_pro_keys, translate_contour_hid_event },
325 { 0x0b33, 0x0020, "Contour ShuttleXPress", mc_shuttle_xpress_keys, translate_contour_hid_event },
326 { 0x0b33, 0x0010, "Contour ShuttlePro", mc_shuttle_pro_keys, translate_contour_hid_event },
327 { 0x0b33, 0x0011, "Contour ShuttlePro", mc_shuttle_pro_keys, translate_contour_hid_event }, /* Hercules OEM */
328 { 0x05f3, 0x0240, "Contour ShuttlePro", mc_shuttle_pro_keys, translate_contour_hid_event },
329 { 0x0760, 0x0001, "JLCooper MCS3", mc_jlcooper_mcs3_keys, translate_compliant },
330 { 0x077d, 0x0410, "Griffin PowerMate", mc_powermate_keys, translate_compliant },
331 { 0x05f3, 0x0241, "X-Keys Editor", mc_x_keys, translate_contour_hid_event },
332 { 0, 0, 0 }
333 };
334
335
media_ctrl_translate(struct media_ctrl * ctrl,struct input_event * ev,struct media_ctrl_event * me)336 void media_ctrl_translate(struct media_ctrl *ctrl, struct input_event *ev, struct media_ctrl_event *me)
337 {
338 if ( ctrl->device ) ctrl->device->translate(ctrl, ev, me);
339 }
340
341
media_ctrl_read_event(struct media_ctrl * ctrl,struct media_ctrl_event * me)342 void media_ctrl_read_event(struct media_ctrl *ctrl, struct media_ctrl_event *me)
343 {
344 ssize_t n;
345 struct input_event ev;
346
347 // struct media_ctrl_event me;
348
349 if ( ctrl->fd > 0 ) {
350 n = read(ctrl->fd, &ev, sizeof(ev));
351 } else {
352 return;
353 }
354
355 if (n != sizeof(ev)) {
356 //printf("JogShuttle::inputCallback: read: (%d) %s\n", errno, strerror(errno));
357 close(ctrl->fd);
358 ctrl->fd = 0;
359 return;
360 }
361
362 if ( ctrl->device && ctrl->device->translate)
363 ctrl->device->translate(ctrl, &ev, me);
364 else
365 me->type = 0;
366
367 if ( me->type == MEDIA_CTRL_EVENT_JOG ) {
368 struct timeval timev;
369 gettimeofday(&timev, NULL);
370 unsigned long now = (unsigned long)timev.tv_usec + (1000000*(unsigned long)timev.tv_sec);
371 if ( now < ctrl->last_jog_time + 40000 ) {
372 //printf("*** Fast Jog %02d %05d ***\n", me->value, now - ctrl->last_jog_time);
373 ctrl->jogrel = me->value;
374 me->type = MEDIA_CTRL_EVENT_NONE;
375 } else {
376 me->value += ctrl->jogrel;
377 ctrl->jogrel = 0;
378 ctrl->last_jog_time = now;
379 // printf("*** Jog %02d ***\n", me->value);
380 }
381 }
382
383 return;
384
385 }
386
387
probe_device(struct media_ctrl * mc,const char * devname)388 int probe_device(struct media_ctrl *mc, const char * devname)
389 {
390 struct input_id devinfo;
391 int i = 0;
392
393 /* suck out the name information
394 * return value is the length of the name, for success
395 * or -EFAULT for failure
396 */
397 char name[256] = "Unknown";
398 if (ioctl(mc->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
399 fprintf(stderr, "%s: ", devname);
400 perror("EVIOCGNAME");
401 }
402
403 if ( ioctl(mc->fd, EVIOCGID, &devinfo) ) {
404 fprintf(stderr, "%s: ", devname);
405 perror("EVIOCGID");
406 return 0;
407 }
408
409 do {
410 if ( supported_devices[i].vendor == devinfo.vendor
411 && supported_devices[i].product == devinfo.product ) {
412
413 mc->device = &supported_devices[i];
414 printf("Success on %s: %s = %s (bus %04x vendor %04x "
415 "product %04x version %04x)\n",
416 devname, name, mc->device->name,
417 devinfo.bustype, devinfo.vendor,
418 devinfo.product, devinfo.version);
419 // mc->fd = fd;
420 // mc->translate = mc->device.translate_function;
421 // mc = malloc(sizeof(struct media_ctrl));
422 mc->jogrel = 0;
423 mc->jogpos = 0;
424 mc->lastval = -1;
425 mc->last_jog_time = 0;
426 return 1;
427 } else {
428 //mc->device = NULL;
429 }
430
431 } while ( supported_devices[++i].vendor != 0 );
432
433 printf("Not interested in %s: %s (bus %04x vendor %04x "
434 "product %04x version %04x)\n",
435 devname, name, devinfo.bustype, devinfo.vendor,
436 devinfo.product, devinfo.version);
437 return 0;
438 }
439
440
media_ctrl_get_device_list()441 void media_ctrl_get_device_list()
442 {
443 // TBD
444 }
445
446
447
find_first_device(struct media_ctrl * mc)448 void find_first_device(struct media_ctrl *mc)
449 {
450 char buf[256];
451 int fd, i;
452
453 for ( i = 0; i < 32; i++ ) {
454 sprintf(buf, "/dev/input/event%d", i);
455 fd = open( buf, O_RDONLY );
456 if ( fd < 0 ) {
457 if (errno != ENOENT)
458 perror(buf);
459 } else {
460 mc->fd = fd;
461 mc->eventno = i;
462 if( probe_device(mc, buf) ) {
463 return;
464 } else {
465 close(fd);
466 mc->fd = -1;
467 }
468 }
469 }
470 return;
471 }
472
473
media_ctrl_close(struct media_ctrl * mc)474 void media_ctrl_close(struct media_ctrl *mc)
475 {
476 if (mc->fd > 0)
477 close( mc->fd );
478 memset( mc, 0, sizeof( struct media_ctrl ) );
479 }
480
481
media_ctrl_open(struct media_ctrl * mc)482 void media_ctrl_open(struct media_ctrl *mc)
483 {
484 find_first_device(mc);
485 }
486 #endif
487