/* -*- indent-tabs-mode:T; c-basic-offset:8; tab-width:8; -*- vi: set ts=8 noet: * $Id: cshuttle.c,v 1.1 2006/04/08 15:22:31 hudson Exp $ * * Contour ShuttlePro v2 interface for Cinelerra. * Based heavily on code by Arendt David * * (c) 2006 Trammell Hudson * * Build with: * gcc -O3 -W -Wall ./cshuttle.c -o cshuttle -L /usr/X11R6/lib -lX11 -lXtst */ #define DELAY 10 #define KEY 1 #define KEY1 256 #define KEY2 257 #define KEY3 258 #define KEY4 259 #define KEY5 260 #define KEY6 261 #define KEY7 262 #define KEY8 263 #define KEY9 264 #define KEY10 265 #define KEY11 266 #define KEY12 267 #define KEY13 268 #define KEY14 269 #define KEY15 270 #define JOGSHUTTLE 2 #define JOG 7 #define SHUTTLE 8 #include #include #include #include #include #include #include #include #include typedef struct input_event EV; unsigned short jogvalue = 0xffff; int shuttlevalue = 0xffff; struct timeval last_shuttle; int need_synthetic_shuttle; Display * display; void initdisplay() { int event, error, major, minor; display = XOpenDisplay(0); if (!display) { fprintf(stderr, "unable to open X display\n"); exit(1); } if (!XTestQueryExtension(display, &event, &error, &major, &minor)) { fprintf(stderr, "Xtest extensions not supported\n"); XCloseDisplay(display); exit(1); } } void sendkey( KeySym modifier, KeySym key ) { KeyCode modifiercode; KeyCode keycode; if (modifier != 0) { modifiercode = XKeysymToKeycode(display, modifier); XTestFakeKeyEvent(display, modifiercode, True, DELAY); } keycode = XKeysymToKeycode(display, key); XTestFakeKeyEvent(display, keycode, True, DELAY); XTestFakeKeyEvent(display, keycode, False, DELAY); if (modifier != 0) XTestFakeKeyEvent(display, modifiercode, False, DELAY); XFlush(display); } /* * The keys on the Contour Shuttle Pro v2 are arranged like this: * * 1 2 3 4 * 5 6 7 8 9 * * 14 Jog 15 * * 10 11 * 12 13 * */ static KeySym button_mappings[][2] = { [ 1] = { XK_Delete }, // Clear the selection [ 5] = { 'v' }, // Splice [ 6] = { 'b' }, // Overwrite [ 9] = { ' ', XK_Control_L }, // Play at insert [10] = { 'z' }, // Undo! [11] = { 'Z', XK_Shift_L }, // Redo! [12] = { XK_Home }, // Start of clip/movie [13] = { XK_End }, // End of clip/movie [14] = { '[', XK_Mode_switch }, // Mark In point [15] = { ']', XK_Mode_switch }, // Mark Out point }; void key( unsigned short code, unsigned int value ) { if( value == 0 ) { // Button release (ignored) return; } code -= KEY1 - 1; // Bound check! if( code > 16 ) return; KeySym * map = button_mappings[code]; if( map[0] == 0 && map[1] == 0 ) return; // Unmapped sendkey( map[1], map[0] ); /* { case KEY5 : sendkey(0, 'z'); break; //undo case KEY14 : sendkey(XK_Mode_switch, '['); break; //begin region case KEY15 : sendkey(XK_Mode_switch, ']'); break; //end region case KEY8 : sendkey(0, 'v'); break; //splice paste case KEY9 : sendkey(0, 'x'); break; //cut } */ } void shuttle( int value ) { gettimeofday( &last_shuttle, 0 ); need_synthetic_shuttle = value != 0; if( value == shuttlevalue ) return; shuttlevalue = value; switch( value ) { case -7 : case -6 : case -5 : case -4 : case -3 : sendkey(0, XK_KP_Add); break; // Reverse fast case -2 : sendkey(0, XK_KP_6); break; // Reverse case -1 : sendkey(0, XK_KP_5); break; // Reverse slow case 0 : sendkey(0, XK_KP_0); break; // Stop! case 1 : sendkey(0, XK_KP_2); break; // Forward slow case 2 : sendkey(0, XK_KP_3); break; // Normal play case 3 : case 4 : case 5 : case 6 : case 7 : sendkey(0, XK_KP_Enter); break; // Fast forward } } /* * Due to a bug (?) in the way Linux HID handles the ShuttlePro, the * center position is not reported for the shuttle wheel. Instead, * a jog event is generated immediately when it returns. We check to * see if the time since the last shuttle was more than a few ms ago * and generate a shuttle of 0 if so. */ void check_synthetic() { if( !need_synthetic_shuttle ) return; struct timeval now, delta; gettimeofday( &now, 0 ); timersub( &now, &last_shuttle, &delta ); if( delta.tv_sec < 1 && delta.tv_usec < 5000 ) return; shuttle( 0 ); need_synthetic_shuttle = 0; } void jog( unsigned int value ) { // We should generate a synthetic event for the shuttle going // to the home position if we have not seen one recently check_synthetic(); if( jogvalue != 0xffff ) { if (value < jogvalue) sendkey( 0, XK_KP_4 ); else if (value > jogvalue) sendkey( 0, XK_KP_1 ); } jogvalue = value; } void jogshuttle( unsigned short code, unsigned int value ) { switch (code) { case JOG : jog(value); break; case SHUTTLE : shuttle(value); break; } } void handle_event( EV ev ) { switch (ev.type) { case KEY : key(ev.code, ev.value); break; case JOGSHUTTLE : jogshuttle(ev.code, ev.value); break; } } int main( int argc, char ** argv ) { char name[256] = "unknown"; EV ev; if (argc < 2) { fprintf( stderr, "syntax: cshuttle \n" ); return EXIT_FAILURE; } //printf("cshuttle 0.1beta written by Arendt David (admin@prnet.org)v 1.1 \n"); const char * dev_name = argv[1]; const int fd = open( dev_name, O_RDONLY ); if (fd < 0) { fprintf( stderr, "unable to open %s\n", dev_name ); return EXIT_FAILURE; } if( ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0 ) { perror("evdev ioctl"); return EXIT_FAILURE; } printf( "%s: %s\n", dev_name, name ); /* Flag it as exclusive access */ if( ioctl( fd, EVIOCGRAB, 1 ) < 0 ) { perror( "evgrab ioctl" ); return EXIT_FAILURE; } initdisplay(); while (1) { read(fd, &ev, sizeof(ev)); handle_event(ev); } close(fd); return 0; }