#include "cstrdup.h"
#include "file.h"
#include "guicast.h"
+#include "keys.h"
#include "linklist.h"
#include "loadfile.h"
#include "mainmenu.h"
}
KeySymMapping KeySymMapping::key_sym_mapping[] = {
+// button keycodes
{ "XK_Button_1", XK_Button_1 },
{ "XK_Button_2", XK_Button_2 },
{ "XK_Button_3", XK_Button_3 },
{ NULL, 0 }
};
-KeySym KeySymMapping::to_keysym(const char *str)
+int KeySymMapping::get_mask(const char *&str)
{
+ int mask = 0;
+ while( *str ) {
+ if( !strncmp("Shift-",str,6) ) {
+ mask |= ShiftMask;
+ str += 6; continue;
+ }
+ if( !strncmp("Ctrl-",str,5) ) {
+ mask |= ControlMask;
+ str += 5; continue;
+ }
+ else if( !strncmp("Alt-",str,4) ) {
+ mask |= Mod1Mask;
+ str += 4; continue;
+ }
+ break;
+ }
+ return mask;
+}
+
+SKeySym KeySymMapping::to_keysym(const char *str)
+{
+ if( !strncmp("FWD_",str, 4) ) {
+ float speed = atof(str+4) / SHUTTLE_MAX_SPEED;
+ if( speed > SHUTTLE_MAX_SPEED ) return 0;
+ int key_code = (SKEY_MAX+SKEY_MIN)/2. +
+ (SKEY_MAX-SKEY_MIN)/2. * speed;
+ if( key_code > SKEY_MAX ) key_code = SKEY_MAX;
+ return key_code;
+ }
+ if( !strncmp("REV_",str, 4) ) {
+ float speed = atof(str+4) / SHUTTLE_MAX_SPEED;
+ if( speed > SHUTTLE_MAX_SPEED ) return 0;
+ int key_code = (SKEY_MAX+SKEY_MIN)/2. -
+ (SKEY_MAX-SKEY_MIN)/2. * speed;
+ if( key_code < SKEY_MIN ) key_code = SKEY_MIN;
+ return key_code;
+ }
+ int mask = get_mask(str);
for( KeySymMapping *ksp = &key_sym_mapping[0]; ksp->str; ++ksp )
- if( !strcmp(str, ksp->str) ) return ksp->sym;
+ if( !strcmp(str, ksp->str) )
+ return SKeySym(ksp->sym, mask);
return 0;
}
-const char *KeySymMapping::to_string(KeySym ks)
+const char *KeySymMapping::to_string(SKeySym ks)
{
- for( KeySymMapping *ksp = &key_sym_mapping[0]; ksp->sym; ++ksp )
- if( ksp->sym == ks ) return ksp->str;
+ for( KeySymMapping *ksp = &key_sym_mapping[0]; ksp->sym.key; ++ksp ) {
+ if( ksp->sym.key == ks.key ) {
+ static char string[BCSTRLEN];
+ char *sp = string, *ep = sp+sizeof(string);
+ if( ks.msk & Mod1Mask ) sp += snprintf(sp, ep-sp, "Alt-");
+ if( ks.msk & ControlMask ) sp += snprintf(sp, ep-sp, "Ctrl-");
+ if( ks.msk & ShiftMask ) sp += snprintf(sp, ep-sp, "Shift-");
+ snprintf(sp, ep-sp, "%s", ksp->str);
+ return string;
+ }
+ }
+ if( ks >= SKEY_MIN && ks <= SKEY_MAX ) {
+ double speed = SHUTTLE_MAX_SPEED *
+ (ks-(SKEY_MAX+SKEY_MIN)/2.) / ((SKEY_MAX-SKEY_MIN)/2.);
+ static char text[BCSTRLEN];
+ sprintf(text, "%s_%0.3f", speed>=0 ? "FWD" : "REV", fabs(speed));
+ char *bp = strchr(text,'.');
+ if( bp ) {
+ char *cp = bp+strlen(bp);
+ while( --cp>bp && *cp=='0' ) *cp=0;
+ if( cp == bp ) *cp = 0;
+ }
+ return text;
+ }
return 0;
}
{
this->cin = cin;
this->name = cstrdup(nm);
- this->err = regcomp(&this->regex, re, REG_NOSUB);
- if( err ) {
- fprintf(stderr, "error compiling regex for [%s]: %s\n", name, re);
- char emsg[BCTEXTLEN];
- regerror(err, ®ex, emsg, sizeof(emsg));
- fprintf(stderr, "regerror: %s\n", emsg);
- }
}
TransName::~TransName()
{
delete [] name;
- regfree(®ex);
}
void Translation::init(int def)
for( int i=0; i<NUM_JOGS; ++i ) jog[i].clear();
}
-void Translation::append_stroke(KeySym sym, int press)
+void Translation::append_stroke(SKeySym sym, int press)
{
Stroke *s = pressed_strokes->append();
s->keysym = sym;
s->press = press;
}
-void Translation::add_keysym(KeySym sym, int press_release)
+void Translation::add_keysym(SKeySym sym, int press_release)
{
//printf("add_keysym(0x%x, %d)\n", (int)sym, press_release);
switch( press_release ) {
{
//printf("add_release(%d)\n", all_keys);
modifiers.release(all_keys);
- if( !all_keys ) {
+ if( !all_keys )
pressed_strokes = released_strokes;
- }
if( keysym_down ) {
append_stroke(keysym_down, 0);
keysym_down = 0;
void Translation::add_keystroke(const char *keySymName, int press_release)
{
- KeySym sym;
+ SKeySym sym;
if( is_key && !strncmp(keySymName, "RELEASE", 8) ) {
add_release(0);
fprintf(stderr, "unrecognized KeySym: %s\n", keySymName);
}
-void Translation::add_string(const char *str)
+void Translation::add_string(char *&str)
{
- while( str && *str ) {
- if( *str >= ' ' && *str <= '~' )
- add_keysym((KeySym)(*str), PRESS_RELEASE);
- ++str;
+ int delim = *str++;
+ if( !delim ) return;
+ while( str && *str && *str!=delim ) {
+ if( *str < ' ' || *str > '~' ) continue;
+ int mask = KeySymMapping::get_mask((const char *&)str);
+ if( str[0] == '\\' && str[1] ) ++str;
+ add_keysym(SKeySym(*str++, mask), PRESS_RELEASE);
+ mask = 0;
}
+ if( *str == delim ) ++str;
}
int Translation::start_line(const char *key)
void Translation::print_line(const char *key)
{
if( is_key ) {
- print_strokes(key, "D", pressed_strokes);
- print_strokes(key, "U", released_strokes);
+ print_strokes(key, "D", pressed);
+ print_strokes(key, "U", released);
}
else {
- print_strokes(key, "", pressed_strokes);
+ print_strokes(key, "", pressed);
}
printf("\n");
}
// PRESS_RELEASE -> released, but to be re-pressed if necessary
// RELEASE -> up
-void Modifiers::mark_as_down(KeySym sym, int hold)
+void Modifiers::mark_as_down(SKeySym sym, int hold)
{
Modifiers &modifiers = *this;
for( int i=0; i<size(); ++i ) {
s.press = hold ? HOLD : PRESS;
}
-void Modifiers::mark_as_up(KeySym sym)
+void Modifiers::mark_as_up(SKeySym sym)
{
Modifiers &modifiers = *this;
for( int i=0; i<size(); ++i ) {
Shuttle::Shuttle(MWindow *mwindow)
- : Thread(0, 0, 0)
+ : Thread(1, 0, 0)
{
this->mwindow = mwindow;
wx = wy = 0;
jogvalue = 0xffff;
shuttlevalue = 0xffff;
- last_shuttle.tv_sec = 0;
- last_shuttle.tv_usec = 0;
- need_synthetic_shuttle = 0;
- dev_name = 0;
+ dev_index = -1;
done = -1;
failed = 0;
first_time = 1;
tr = 0;
+ debug = 0;
+ usb_direct = 0;
+
last_translation = 0;
last_focused = 0;
+#ifdef HAVE_SHUTTLE_USB
+ devsh = 0;
+ claimed = -1;
+ last_jog = 0;
+ last_shuttle = 0;
+ last_btns = 0;
+#endif
+
default_translation = new Translation(this);
config_path = 0;
config_mtime = 0;
memset(b, 0, sizeof(*b));
b->type = press ? ButtonPress : ButtonRelease;
b->time = milliTimeClock();
+ b->send_event = 1;
b->display = wdw->top_level->display;
b->root = wdw->top_level->rootwin;
b->window = win;
wdw->top_level->put_event((XEvent *) b);
return 0;
}
-int Shuttle::send_key(KeySym keysym, int press)
+int Shuttle::send_keycode(unsigned key, unsigned msk, int press, int send)
{
- KeyCode keycode = XKeysymToKeycode(wdw->top_level->display, keysym);
- if( debug )
- printf("key: %04x %d\n", (unsigned)keycode, press);
+ if( debug ) {
+ const char *cp = !send ? 0 :
+ KeySymMapping::to_string(SKeySym(key, msk));
+ if( cp )
+ printf("key: %s %d\n", cp, press);
+ else
+ printf("key: %04x/%04x %d\n", key, msk, press);
+ }
XKeyEvent *k = new XKeyEvent();
memset(k, 0, sizeof(*k));
k->type = press ? KeyPress : KeyRelease;
k->time = milliTimeClock();
+ k->send_event = send;
k->display = wdw->top_level->display;
k->root = wdw->top_level->rootwin;
k->window = win;
k->x = wx;
k->y = wy;
k->state = msk;
- k->keycode = keycode;
+ k->keycode = key;
k->same_screen = 1;
wdw->top_level->put_event((XEvent *) k);
return 0;
}
-int Shuttle::send_keysym(KeySym keysym, int press)
+int Shuttle::send_keysym(SKeySym keysym, int press)
{
return keysym >= XK_Button_1 && keysym <= XK_Scroll_Down ?
send_button((unsigned int)keysym - XK_Button_0, press) :
- send_key(keysym, press ? True : False);
+ send_keycode(keysym.key, keysym.msk, press, 1);
+// unsigned int keycode = XKeysymToKeycode(wdw->top_level->display, keysym);
+// return send_keycode(keycode, press, 0);
}
if( translation ) {
switch( kjs ) {
default:
- case KJS_KEY_DOWN: ret = translation->key_down[index].first; break;
- case KJS_KEY_UP: ret = translation->key_up[index].first; break;
- case KJS_JOG: ret = translation->jog[index].first; break;
- case KJS_SHUTTLE: ret = translation->shuttles[index].first; break;
+ case KJS_KEY_DOWN: ret = translation->key_down[index].first; break;
+ case KJS_KEY_UP: ret = translation->key_up[index].first; break;
+ case KJS_JOG: ret = translation->jog[index].first; break;
+ case KJS_SHUTTLE: ret = translation->shuttles[index-S_7].first; break;
}
}
return ret;
}
-void Shuttle:: shuttle(int value)
+void Shuttle::shuttle(int value)
{
if( value < S_7 || value > S7 ) {
fprintf(stderr, "shuttle(%d) out of range\n", value);
return;
}
- gettimeofday(&last_shuttle, 0);
- need_synthetic_shuttle = value != 0;
- if( value != shuttlevalue ) {
+ if( value != (int)shuttlevalue ) {
shuttlevalue = value;
- send_stroke_sequence(KJS_SHUTTLE, value+7);
+ send_stroke_sequence(KJS_SHUTTLE, value);
}
}
// event either!
void Shuttle::jog(unsigned int value)
{
- 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);
- need_synthetic_shuttle = 0;
- }
- }
-
if( jogvalue != 0xffff ) {
value = value & 0xff;
- direction = ((value - jogvalue) & 0x80) ? -1 : 1;
+ int direction = ((value - jogvalue) & 0x80) ? -1 : 1;
+ int index = direction > 0 ? 1 : 0;
while( jogvalue != value ) {
// driver fails to send an event when jogvalue == 0
if( jogvalue != 0 ) {
- send_stroke_sequence(KJS_JOG, direction > 0 ? 1 : 0);
+ send_stroke_sequence(KJS_JOG, index);
}
jogvalue = (jogvalue + direction) & 0xff;
}
case EVENT_CODE_SHUTTLE:
shuttle(value);
break;
+ case EVENT_CODE_HI_JOG: // redundant report of JOG value*120
+ break;
default:
fprintf(stderr, "jogshuttle(%d, %d) invalid code\n", code, value);
break;
}
}
-const char *Shuttle::probe()
+static const struct shuttle_dev {
+ const char *path;
+ unsigned vendor, product;
+} shuttle_devs[] = {
+ { "/dev/input/by-id/usb-Contour_Design_ShuttleXpress-event-if00",
+ 0x0b33, 0x0020 },
+ { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-if00",
+ 0x0b33, 0x0030 },
+ { "/dev/input/by-id/usb-Contour_Design_ShuttlePro-event-if00",
+ 0x0b33, 0x0030 },
+ { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-joystick",
+ 0x0b33, 0x0030 },
+};
+
+#ifdef HAVE_SHUTTLE_USB
+void Shuttle::usb_probe(int idx)
+{
+ int ret = libusb_init(0);
+ if( ret < 0 ) return;
+ claimed = 0;
+ const struct shuttle_dev *s = &shuttle_devs[idx];
+ devsh = libusb_open_device_with_vid_pid(0, s->vendor, s->product);
+ if( devsh ) {
+ int sh_iface = SHUTTLE_INTERFACE;
+ libusb_detach_kernel_driver(devsh, sh_iface);
+ ret = libusb_claim_interface(devsh, sh_iface);
+ if( ret >= 0 ) claimed = 1;
+ }
+ if( !claimed )
+ usb_done();
+}
+
+void Shuttle::usb_done()
+{
+ if( devsh ) {
+ if( claimed > 0 ) {
+ int sh_iface = SHUTTLE_INTERFACE;
+ libusb_release_interface(devsh, sh_iface);
+ libusb_attach_kernel_driver(devsh, sh_iface);
+ claimed = 0;
+ }
+ libusb_close(devsh);
+ devsh = 0;
+ }
+ if( claimed >= 0 ) {
+ libusb_exit(0);
+ claimed = -1;
+ }
+}
+#endif
+
+int Shuttle::probe()
{
struct stat st;
- static const char *shuttle_devs[] = {
- "/dev/input/by-id/usb-Contour_Design_ShuttleXpress-event-if00",
- "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-if00",
- };
int ret = sizeof(shuttle_devs) / sizeof(shuttle_devs[0]);
- while( --ret >= 0 && stat(shuttle_devs[ret] , &st) );
- return ret >= 0 ? shuttle_devs[ret] : 0;
+ while( --ret >= 0 && stat(shuttle_devs[ret].path , &st) );
+ return ret;
}
-void Shuttle::start(const char *dev_name)
+void Shuttle::start(int idx)
{
- this->dev_name = dev_name;
+ this->dev_index = idx;
first_time = 1;
done = 0;
Thread::start();
}
}
+BC_WindowBase *Shuttle::owns(BC_WindowBase *wdw, Window win)
+{
+ if( wdw->win == win ) return wdw;
+ if( (wdw=wdw->top_level)->win == win ) return wdw;
+ for( int i=wdw->popups.size(); --i>=0; )
+ if( wdw->popups[i]->win == win ) return wdw;
+ return 0;
+}
int Shuttle::get_focused_window_translation()
{
Display *dpy = gui->display;
Window focus = 0;
int ret = 0, revert = 0;
- char win_title[BCTEXTLEN];
+ char win_title[BCTEXTLEN]; win_title[0] = 0;
gui->lock_window("Shuttle::get_focused_window_translation");
XGetInputFocus(dpy, &focus, &revert);
if( last_focused != focus ) {
win_title[len] = 0;
XFree(list);
if( debug )
- printf("new focus: %08x\n", (unsigned)focus);
+ printf("new focus: %08x %s\n", (unsigned)focus, win_title);
}
else {
last_focused = 0;
this->msk = 0;
BC_WindowBase *wdw = 0;
int cin = -1;
- if( (wdw=mwindow->gui) && wdw->win == focus )
+ if( (wdw=owns(mwindow->gui, focus)) != 0 )
cin = FOCUS_MWINDOW;
- else if( (wdw=mwindow->awindow->gui) && wdw->win == focus )
+ else if( (wdw=owns(mwindow->awindow->gui, focus)) != 0 )
cin = FOCUS_AWINDOW;
- else if( (wdw=mwindow->cwindow->gui) && wdw->win == focus )
+ else if( (wdw=owns(mwindow->cwindow->gui, focus)) != 0 ) {
+ if( mwindow->cwindow->gui->canvas->get_fullscreen() )
+ wdw = mwindow->cwindow->gui->canvas->get_canvas();
cin = FOCUS_CWINDOW;
- else if( (wdw=mwindow->gui->mainmenu->load_file->thread->window) &&
+ }
+ else if( mwindow->gui->mainmenu->load_file->thread->running() &&
+ (wdw=mwindow->gui->mainmenu->load_file->thread->window) != 0 &&
wdw->win == focus )
cin = FOCUS_LOAD;
else {
while( --i >= 0 ) {
VWindow *vwdw = mwindow->vwindows[i];
if( !vwdw->is_running() ) continue;
- if( (wdw=vwdw->gui) && wdw->win == focus ) {
+ if( (wdw=owns(vwdw->gui, focus)) != 0 ) {
+ if( vwdw->gui->canvas->get_fullscreen() )
+ wdw = vwdw->gui->canvas->get_canvas();
cin = FOCUS_VIEWER; break;
}
}
int root_x = 0, root_y = 0, win_x = 0, win_y = 0, x = 0, y = 0;
unsigned int mask = 0, width = 0, height = 0, border_width = 0, depth = 0;
wdw->lock_window("Shuttle::get_focused_window_translation 1");
- if( XQueryPointer(wdw->display, focus, &root, &child,
+ if( XQueryPointer(wdw->top_level->display, focus, &root, &child,
&root_x, &root_y, &win_x, &win_y, &mask) ) {
if( !child ) {
if( wdw->active_menubar )
child = wdw->active_popup_menu->win;
else if( wdw->active_subwindow )
child = wdw->active_subwindow->win;
+ else
+ child = wdw->win;
}
- if( child )
- XGetGeometry(wdw->display, child, &root, &x, &y,
+ else
+ XGetGeometry(wdw->top_level->display, child, &root, &x, &y,
&width, &height, &border_width, &depth);
}
wdw->unlock_window();
if( tr->is_default ) return 1;
for( int i=0; i<tr->names.size(); ++i ) {
TransName *name = tr->names[i];
- if( name->cin != cin ) continue;
- if( regexec(&name->regex, win_title, 0, NULL, 0) )
- return 1;
+ if( name->cin == cin ) return 1;
}
}
tr = default_translation;
return 0;
}
-void Shuttle::handle_event()
+int Shuttle::load_translation()
{
- if( read_config_file() > 0 ) {
- done = 1;
- return;
- }
+ if( read_config_file() > 0 )
+ return done = 1;
if( get_focused_window_translation() < 0 )
- return;
+ return 0;
if( last_translation != tr ) {
last_translation = tr;
if( debug )
printf("new translation: %s\n", tr->name);
}
-//fprintf(stderr, "event: (%d, %d, 0x%x)\n", ev.type, ev.code, ev.value);
+ return 0;
+}
+
+void Shuttle::handle_event()
+{
+ if( load_translation() ) return;
+// if( debug )
+// printf("event: (%d, %d, 0x%x)\n", ev.type, ev.code, ev.value);
switch( ev.type ) {
case EVENT_TYPE_DONE:
case EVENT_TYPE_ACTIVE_KEY:
void Shuttle::run()
{
- for( enable_cancel(); !done; sleep(1) ) {
+ if( dev_index < 0 ) return;
+ const char *dev_name = shuttle_devs[dev_index].path;
+
+#ifdef HAVE_SHUTTLE_USB
+ if( usb_direct )
+ usb_probe(dev_index);
+
+ disable_cancel();
+ while( devsh && !done ) {
+ int len = 0;
+ static const int IN_ENDPOINT = 0x81;
+ unsigned char dat[5];
+ int ret = libusb_interrupt_transfer(devsh,
+ IN_ENDPOINT, dat, sizeof(dat), &len, 100);
+ if( ret != 0 ) {
+ if( ret == LIBUSB_ERROR_TIMEOUT ) continue;
+ printf("shuttle: %s\n %s\n",
+ dev_name, libusb_strerror((libusb_error)ret));
+ sleep(1); continue;
+ }
+ if( load_translation() ) break;
+ if( debug ) {
+ printf("shuttle: ");
+ for( int i=0; i<len; ++i ) printf(" %02x", dat[i]);
+ printf("\n");
+ }
+ if( last_shuttle != dat[0] )
+ shuttle((char)(last_shuttle = dat[0]));
+
+ if( last_jog != dat[1] )
+ jog(last_jog = dat[1]);
+
+ unsigned btns = (dat[4]<<8) | dat[3];
+ unsigned dif = last_btns ^ btns;
+ if( dif ) {
+ last_btns = btns;
+ for( int i=0; i<15; ++i ) {
+ unsigned msk = 1 << i;
+ if( !(dif & msk) ) continue;
+ key(i+EVENT_CODE_KEY1, btns & msk ? 1 : 0);
+ }
+ }
+ }
+ usb_done();
+#endif
+ usb_direct = 0;
+ enable_cancel();
+
+ for( ; !done; sleep(1) ) {
fd = open(dev_name, O_RDONLY);
if( fd < 0 ) {
perror(dev_name);
if( first_time ) break;
continue;
}
- if( !ioctl(fd, EVIOCGRAB, 1) ) { // exclusive access
+ if( 1 || !ioctl(fd, EVIOCGRAB, 1) ) { // exclusive access
first_time = 0;
while( !done ) {
int ret = read(fd, &ev, sizeof(ev));
config_mtime = st.st_mtime;
translations.clear();
debug = 0;
+ usb_direct = 0;
#define ws(ch) (ch==' ' || ch=='\t')
char line[BCTEXTLEN], *cp;
Translation *trans = 0;
}
}
while( ws(*cp) ) ++cp;
-// regex in TransName constructor
trans->names.append(new TransName(cin, name, cp));
- if( trans->names.last()->err ) { ret = 1; break; }
ret = fgets(cp=line,sizeof(line),fp) ? 0 : 1;
if( ret ) {
fprintf(stderr, "hit eof, no translation def for: %s\n",
if( !strcmp(tok, "DEBUG") ) {
debug = 1; goto skip;
}
+ if( !strcmp(tok, "USB_DIRECT") ) {
+ usb_direct = 1; goto skip;
+ }
key = tok;
if( !trans ) {
fprintf(stderr, "no translation section defining key: %s\n", key);
while( ws(*cp) ) ++cp;
if( !*cp || *cp == '#' || *cp == '\n' ) break;
if( *cp == '"' ) {
- tok = ++cp;
- while( *cp && *cp != '"' && *cp != '\n' ) {
- if( *cp != '\\' ) { ++cp; continue; }
- for( char *bp=cp; *bp; ++bp ) bp[0] = bp[1];
+ while( *cp ) {
+ if( *cp == '"' )
+ trans->add_string(cp);
+ while( ws(*cp) ) ++cp;
+ if( !*cp || *cp == '#' || *cp == '\n' ) break;
+ tok = cp;
+ while( *cp && !ws(*cp) && *cp != '\n' ) ++cp;
+ *cp = 0;
+ SKeySym sym = KeySymMapping::to_keysym(tok);
+ if( !sym ) {
+ fprintf(stderr, "unknown keysym: %s\n", tok);
+ ret = 1; break;
+ }
+ trans->add_keysym(sym, PRESS_RELEASE);
}
- *cp++ = 0;
- trans->add_string(tok);
continue;
}
tok = cp;