ShuttlePRO/shuttlepro.c

341 wiersze
7.1 KiB
C

/*
Contour ShuttlePro v2 interface
Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact)
Based on a version (c) 2006 Trammell Hudson <hudson@osresearch.net>
which was in turn
Based heavily on code by Arendt David <admin@prnet.org>
*/
#include "shuttle.h"
typedef struct input_event EV;
extern int debug_regex;
extern translation *default_translation;
unsigned short jogvalue = 0xffff;
int shuttlevalue = 0xffff;
struct timeval last_shuttle;
int need_synthetic_shuttle;
Display *display;
void
initdisplay(void)
{
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
send_button(unsigned int button, int press)
{
XTestFakeButtonEvent(display, button, press ? True : False, DELAY);
}
void
send_key(KeySym key, int press)
{
KeyCode keycode;
if (key >= XK_Button_1 && key <= XK_Scroll_Down) {
send_button((unsigned int)key - XK_Button_0, press);
return;
}
keycode = XKeysymToKeycode(display, key);
XTestFakeKeyEvent(display, keycode, press ? True : False, DELAY);
}
stroke *
fetch_stroke(translation *tr, int kjs, int index)
{
if (tr != NULL) {
switch (kjs) {
case KJS_SHUTTLE:
return tr->shuttle[index];
case KJS_JOG:
return tr->jog[index];
case KJS_KEY_UP:
return tr->key_up[index];
case KJS_KEY_DOWN:
default:
return tr->key_down[index];
}
}
return NULL;
}
void
send_stroke_sequence(translation *tr, int kjs, int index)
{
stroke *s;
s = fetch_stroke(tr, kjs, index);
if (s == NULL) {
s = fetch_stroke(default_translation, kjs, index);
}
while (s) {
send_key(s->keysym, s->press);
s = s->next;
}
XFlush(display);
}
void
key(unsigned short code, unsigned int value, translation *tr)
{
code -= EVENT_CODE_KEY1;
if (code <= NUM_KEYS) {
send_stroke_sequence(tr, value ? KJS_KEY_DOWN : KJS_KEY_UP, code);
} else {
fprintf(stderr, "key(%d, %d) out of range\n", code + EVENT_CODE_KEY1, value);
}
}
void
shuttle(int value, translation *tr)
{
if (value < -7 || value > 7) {
fprintf(stderr, "shuttle(%d) out of range\n", value);
} else {
gettimeofday(&last_shuttle, 0);
need_synthetic_shuttle = value != 0;
if( value != shuttlevalue ) {
shuttlevalue = value;
send_stroke_sequence(tr, KJS_SHUTTLE, value+7);
}
}
}
// 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.
//
// Note, this fails if jogvalue happens to be 0, as we don't see that
// event either!
void
jog(unsigned int value, translation *tr)
{
int direction;
struct timeval now;
struct timeval delta;
// We should generate a synthetic event for the shuttle going
// to the home position if we have not seen one recently
if (need_synthetic_shuttle) {
gettimeofday( &now, 0 );
timersub( &now, &last_shuttle, &delta );
if (delta.tv_sec >= 1 || delta.tv_usec >= 5000) {
shuttle(0, tr);
need_synthetic_shuttle = 0;
}
}
if (jogvalue != 0xffff) {
value = value & 0xff;
direction = ((value - jogvalue) & 0x80) ? -1 : 1;
while (jogvalue != value) {
// driver fails to send an event when jogvalue == 0
if (jogvalue != 0) {
send_stroke_sequence(tr, KJS_JOG, direction > 0 ? 1 : 0);
}
jogvalue = (jogvalue + direction) & 0xff;
}
}
jogvalue = value;
}
void
jogshuttle(unsigned short code, unsigned int value, translation *tr)
{
switch (code) {
case EVENT_CODE_JOG:
jog(value, tr);
break;
case EVENT_CODE_SHUTTLE:
shuttle(value, tr);
break;
default:
fprintf(stderr, "jogshuttle(%d, %d) invalid code\n", code, value);
break;
}
}
char *
get_window_name(Window win)
{
Atom prop = XInternAtom(display, "WM_NAME", False);
Atom type;
int form;
unsigned long remain, len;
unsigned char *list;
if (XGetWindowProperty(display, win, prop, 0, 1024, False,
AnyPropertyType, &type, &form, &len, &remain,
&list) != Success) {
fprintf(stderr, "XGetWindowProperty failed for window 0x%x\n", (int)win);
return NULL;
}
return (char*)list;
}
char *
walk_window_tree(Window win)
{
char *window_name;
Window root = 0;
Window parent;
Window *children;
unsigned int nchildren;
while (win != root) {
window_name = get_window_name(win);
if (window_name != NULL) {
return window_name;
}
if (XQueryTree(display, win, &root, &parent, &children, &nchildren)) {
win = parent;
XFree(children);
} else {
fprintf(stderr, "XQueryTree failed for window 0x%x\n", (int)win);
return NULL;
}
}
return NULL;
}
static Window last_focused_window = 0;
static translation *last_window_translation = NULL;
translation *
get_focused_window_translation()
{
Window focus;
int revert_to;
char *window_name = NULL;
char *name;
XGetInputFocus(display, &focus, &revert_to);
if (focus != last_focused_window) {
last_focused_window = focus;
window_name = walk_window_tree(focus);
if (window_name == NULL) {
name = "-- Unlabeled Window --";
} else {
name = window_name;
}
last_window_translation = get_translation(name);
if (debug_regex) {
if (last_window_translation != NULL) {
printf("translation: %s for %s\n", last_window_translation->name, name);
} else {
printf("no translation found for %s\n", name);
}
}
if (window_name != NULL) {
XFree(window_name);
}
}
return last_window_translation;
}
void
handle_event(EV ev)
{
translation *tr = get_focused_window_translation();
//fprintf(stderr, "event: (%d, %d, 0x%x)\n", ev.type, ev.code, ev.value);
if (tr != NULL) {
switch (ev.type) {
case EVENT_TYPE_DONE:
case EVENT_TYPE_ACTIVE_KEY:
break;
case EVENT_TYPE_KEY:
key(ev.code, ev.value, tr);
break;
case EVENT_TYPE_JOGSHUTTLE:
jogshuttle(ev.code, ev.value, tr);
break;
default:
fprintf(stderr, "handle_event() invalid type code\n");
break;
}
}
}
int
main(int argc, char **argv)
{
EV ev;
int nread;
char *dev_name;
int fd;
int first_time = 1;
if (argc != 2) {
fprintf(stderr, "usage: shuttlepro <device>\n" );
exit(1);
}
dev_name = argv[1];
initdisplay();
while (1) {
fd = open(dev_name, O_RDONLY);
if (fd < 0) {
perror(dev_name);
if (first_time) {
exit(1);
}
} else {
// Flag it as exclusive access
if(ioctl( fd, EVIOCGRAB, 1 ) < 0) {
perror( "evgrab ioctl" );
} else {
first_time = 0;
while (1) {
nread = read(fd, &ev, sizeof(ev));
if (nread == sizeof(ev)) {
handle_event(ev);
} else {
if (nread < 0) {
perror("read event");
break;
} else {
fprintf(stderr, "short read: %d\n", nread);
break;
}
}
}
}
}
close(fd);
sleep(1);
}
}