X-Git-Url: https://git.cinelerra-gg.org/git/?p=goodguy%2Fcinelerra.git;a=blobdiff_plain;f=cinelerra-5.1%2Fcinelerra%2Fshuttle.C;h=df2850dc8e4f22a5c6915e7062ddb151378b74d4;hp=627f162a8d6f0e71a5f65bfbc33940f1fa970208;hb=ab958f323d68316c450ef81ba296536580620163;hpb=2a56d102d9db53017b306f2a61a2382f29a75783 diff --git a/cinelerra-5.1/cinelerra/shuttle.C b/cinelerra-5.1/cinelerra/shuttle.C index 627f162a..df2850dc 100644 --- a/cinelerra-5.1/cinelerra/shuttle.C +++ b/cinelerra-5.1/cinelerra/shuttle.C @@ -2,22 +2,33 @@ // Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact) // reworked 2019 for cinelerra-gg by William Morrow -// keys.h collides with linux/input_events.h -#define KEYS_H - #include "arraylist.h" #include "cstrdup.h" #include "file.h" #include "guicast.h" +#include "keys.h" #include "linklist.h" #include "loadfile.h" #include "mainmenu.h" -#include "mwindow.h" #include "shuttle.h" #include "thread.h" +#include "mwindow.h" +#include "mwindowgui.h" +#include "awindow.h" +#include "awindowgui.h" +#include "cwindow.h" +#include "cwindowgui.h" +#include "vwindow.h" +#include "vwindowgui.h" + +#include +#include +#include +#include +#include +#include #include -#include #include #include @@ -31,6 +42,7 @@ static Time milliTimeClock() } 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 }, @@ -40,17 +52,78 @@ KeySymMapping KeySymMapping::key_sym_mapping[] = { { 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; } @@ -58,18 +131,10 @@ TransName::TransName(int cin, const char *nm, const char *re) { 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) @@ -124,14 +189,14 @@ void Translation::clear() for( int i=0; iappend(); 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 ) { @@ -166,9 +231,8 @@ void Translation::add_release(int all_keys) { //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; @@ -178,7 +242,7 @@ void Translation::add_release(int all_keys) void Translation::add_keystroke(const char *keySymName, int press_release) { - KeySym sym; + SKeySym sym; if( is_key && !strncmp(keySymName, "RELEASE", 8) ) { add_release(0); @@ -192,13 +256,18 @@ void Translation::add_keystroke(const char *keySymName, int press_release) 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) @@ -273,11 +342,11 @@ void Translation::finish_line() 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"); } @@ -288,7 +357,7 @@ void Translation::print_line(const char *key) // 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; imwindow = mwindow; @@ -357,18 +426,26 @@ Shuttle::Shuttle(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; @@ -390,6 +467,7 @@ int Shuttle::send_button(unsigned int button, int press) 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; @@ -403,15 +481,21 @@ int Shuttle::send_button(unsigned int button, int press) 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; @@ -420,17 +504,19 @@ int Shuttle::send_key(KeySym keysym, int press) 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); } @@ -440,10 +526,10 @@ static Stroke *fetch_stroke(Translation *translation, int kjs, int index) 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; @@ -471,17 +557,15 @@ void Shuttle::key(unsigned short code, unsigned int value) } -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); } } @@ -495,29 +579,14 @@ void Shuttle:: shuttle(int 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; } @@ -540,21 +609,68 @@ void Shuttle::jogshuttle(unsigned short code, unsigned int value) } } -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(); @@ -569,6 +685,14 @@ void Shuttle::stop() } } +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() { @@ -576,7 +700,7 @@ 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 ) { @@ -592,7 +716,7 @@ int Shuttle::get_focused_window_translation() 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; @@ -610,13 +734,17 @@ int Shuttle::get_focused_window_translation() 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 { @@ -624,7 +752,9 @@ int Shuttle::get_focused_window_translation() 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; } } @@ -634,7 +764,7 @@ int Shuttle::get_focused_window_translation() 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 ) @@ -643,9 +773,11 @@ int Shuttle::get_focused_window_translation() 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(); @@ -662,29 +794,32 @@ int Shuttle::get_focused_window_translation() if( tr->is_default ) return 1; for( int i=0; inames.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: @@ -703,7 +838,55 @@ void Shuttle::handle_event() 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; inames.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", @@ -835,6 +1017,9 @@ int Shuttle::read_config_file() 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); @@ -846,13 +1031,21 @@ int Shuttle::read_config_file() 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;