add shuttle dev support, use dflt ff_a/v.png icons, mjpegtools typo patch
authorGood Guy <good1.2guy@gmail.com>
Tue, 29 Jan 2019 19:39:37 +0000 (12:39 -0700)
committerGood Guy <good1.2guy@gmail.com>
Tue, 29 Jan 2019 19:39:37 +0000 (12:39 -0700)
33 files changed:
cinelerra-5.1/Makefile.am
cinelerra-5.1/cinelerra/Makefile
cinelerra-5.1/cinelerra/awindowgui.C
cinelerra-5.1/cinelerra/loadfile.C
cinelerra-5.1/cinelerra/mwindow.C
cinelerra-5.1/cinelerra/mwindow.h
cinelerra-5.1/cinelerra/shuttle.C [new file with mode: 0644]
cinelerra-5.1/cinelerra/shuttle.h [new file with mode: 0644]
cinelerra-5.1/cinelerra/shuttle.inc [new file with mode: 0644]
cinelerra-5.1/cinelerra/shuttle.sed [new file with mode: 0644]
cinelerra-5.1/configure.ac
cinelerra-5.1/guicast/bcwindowbase.h
cinelerra-5.1/guicast/bcwindowbase.inc
cinelerra-5.1/plugins/theme_blond/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_blond/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_blond_cv/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_blond_cv/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_blue/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_blue/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_blue_dot/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_blue_dot/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_bright/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_bright/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_hulk/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_hulk/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_pinklady/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_pinklady/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_suv/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_suv/data/ff_video.png [deleted file]
cinelerra-5.1/plugins/theme_unflat/data/ff_audio.png [deleted file]
cinelerra-5.1/plugins/theme_unflat/data/ff_video.png [deleted file]
cinelerra-5.1/shuttlerc [new file with mode: 0644]
cinelerra-5.1/thirdparty/src/mjpegtools-2.1.0.patch3 [new file with mode: 0644]

index a61379c..603f285 100644 (file)
@@ -33,7 +33,7 @@ export mkinstalldirs install_sh inst_sh
 # install to bin
 bin_install:   install-recursive
        cp -a COPYING README models Cinelerra_factory expanders.txt \
-               ffmpeg msg info tips lv2_blacklist.txt bin/.
+               shuttlerc ffmpeg msg info tips lv2_blacklist.txt bin/.
        sed -e 's/\<cin\>/$(WANT_CIN)/g' < image/cin.desktop \
                > "bin/applications/$(WANT_CIN).desktop"
        cp -a image/cin.svg "bin/pixmaps/$(WANT_CIN)".svg
@@ -53,7 +53,7 @@ sys_install: $(CIN_INSTALLS)
        cd bin; $(inst_sh) "$(DESTDIR)$(bindir)" "$(WANT_CIN)" bdwrite
        cd bin; $(inst_sh) "$(DESTDIR)$(datadir)/$(WANT_CIN)" \
                COPYING README models Cinelerra_factory expanders.txt \
-               ffmpeg msg info tips doc
+               shuttlerc ffmpeg msg info tips doc
        cd bin/locale; $(inst_sh) "$(DESTDIR)$(localedir)" .
        cd bin/plugins; $(inst_sh) "$(DESTDIR)$(WANT_PLUGIN_DIR)" .
        cd bin/applications; $(inst_sh) "$(DESTDIR)$(datadir)/applications" .
index 43c66e8..4c08bbf 100644 (file)
@@ -271,6 +271,7 @@ OBJS := \
        $(OBJDIR)/sharedlocation.o \
        $(OBJDIR)/shbtnprefs.o \
        $(OBJDIR)/shmemory.o \
+       $(OBJDIR)/shuttle.o \
        $(OBJDIR)/sighandler.o \
        $(OBJDIR)/signalstatus.o \
        $(OBJDIR)/splashgui.o \
@@ -582,6 +583,11 @@ $(OBJDIR)/lv2ui.o: lv2ui.C
 $(OBJDIR)/pluginlv2ui.o $(OBJDIR)/lv2ui.o:
        $(CXX) `cat $(OBJDIR)/c_flags` $(GTK2_INCS) -DMSGQUAL=$* -c $< -o $@
 
+$(OBJDIR)/shuttle.o:   shuttle.C shuttle_keys.h
+       $(CXX) `cat $(OBJDIR)/c_flags` -DMSGQUAL=$* -c $< -o $@
+shuttle_keys.h: /usr/include/X11/keysymdef.h
+       sed < /usr/include/X11/keysymdef.h > shuttle_keys.h -f shuttle.sed
+
 $(OBJDIR)/lv2ui: $(LV2OBJS)
        @echo $(CXX) \`cat $(OBJDIR)/c_flags\` $^ -o $@
        @$(CXX) `cat $(OBJDIR)/c_flags` $^ -o $@ $(LIBS) $(GTK2_LIBS)
index a5e4007..2597f52 100644 (file)
@@ -1213,14 +1213,6 @@ AWindowGUI::AWindowGUI(MWindow *mwindow, AWindow *awindow)
        video_vframe = 0;               video_icon = 0;
        label_vframe = 0;               label_icon = 0;
 
-       atransition_vframe = 0;         atransition_icon = 0;
-       vtransition_vframe = 0;         vtransition_icon = 0;
-       aeffect_vframe = 0;             aeffect_icon = 0;
-       ladspa_vframe = 0;              ladspa_icon = 0;
-       veffect_vframe = 0;             veffect_icon = 0;
-       ff_aud_vframe = 0;              ff_aud_icon = 0;
-       ff_vid_vframe = 0;              ff_vid_icon = 0;
-
        aeffect_folder_vframe = 0;      aeffect_folder_icon = 0;
        atransition_folder_vframe = 0;  atransition_folder_icon = 0;
        clip_folder_vframe = 0;         clip_folder_icon = 0;
index 2ec06fb..345456d 100644 (file)
@@ -47,6 +47,7 @@ Load::Load(MWindow *mwindow, MainMenu *mainmenu)
 {
        this->mwindow = mwindow;
        this->mainmenu = mainmenu;
+       this->thread = 0;
 }
 
 Load::~Load()
@@ -77,6 +78,7 @@ LoadFileThread::LoadFileThread(MWindow *mwindow, Load *load)
 {
        this->mwindow = mwindow;
        this->load = load;
+       this->window = 0;
 }
 
 LoadFileThread::~LoadFileThread()
index 9ad32f6..af743cc 100644 (file)
 #include "savefile.inc"
 #include "samplescroll.h"
 #include "sha1.h"
+#include "shuttle.h"
 #include "sighandler.h"
 #include "splashgui.h"
 #include "statusbar.h"
@@ -237,6 +238,7 @@ MWindow::MWindow()
        in_destructor = 0;
        speed_edl = 0;
        proxy_beep = 0;
+       shuttle = 0;
 }
 
 
@@ -254,6 +256,7 @@ MWindow::~MWindow()
        delete proxy_beep;
        delete create_bd;       create_bd = 0;
        delete create_dvd;      create_dvd = 0;
+       delete shuttle;         shuttle = 0;
        delete batch_render;    batch_render = 0;
        delete render;          render = 0;
        commit_commercial();
@@ -1582,6 +1585,17 @@ void MWindow::init_exportedl()
        exportedl = new ExportEDL(this);
 }
 
+void MWindow::init_shuttle()
+{
+#ifdef HAVE_SHUTTLE
+       const char *dev_name = Shuttle::probe();
+       if( dev_name ) {
+               shuttle = new Shuttle(this);
+               shuttle->start(dev_name);
+       }
+#endif
+}
+
 void MWindow::init_brender()
 {
        if(preferences->use_brender && !brender)
@@ -2620,6 +2634,7 @@ void MWindow::create_objects(int want_gui,
        init_render();
        if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
 
+       init_shuttle();
        init_brender();
        init_exportedl();
        init_commercials();
index ce85ca9..7950659 100644 (file)
@@ -82,6 +82,7 @@
 #include "sharedlocation.inc"
 #include "sighandler.inc"
 #include "splashgui.inc"
+#include "shuttle.inc"
 #include "theme.inc"
 #include "thread.h"
 #include "threadloader.inc"
@@ -706,6 +707,7 @@ public:
 
        void init_preferences();
        void init_signals();
+       void init_shuttle();
        void init_theme();
        void init_compositor();
        void init_levelwindow();
@@ -730,6 +732,7 @@ public:
        int restart_status;
        int screens;
        int in_destructor;
+       Shuttle *shuttle;
 };
 
 #endif
diff --git a/cinelerra-5.1/cinelerra/shuttle.C b/cinelerra-5.1/cinelerra/shuttle.C
new file mode 100644 (file)
index 0000000..627f162
--- /dev/null
@@ -0,0 +1,898 @@
+#ifdef HAVE_SHUTTLE
+// 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 "linklist.h"
+#include "loadfile.h"
+#include "mainmenu.h"
+#include "mwindow.h"
+#include "shuttle.h"
+#include "thread.h"
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+static Time milliTimeClock()
+{
+       struct timeval tv;
+       gettimeofday(&tv, 0);
+       return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
+
+KeySymMapping KeySymMapping::key_sym_mapping[] = {
+       { "XK_Button_1", XK_Button_1 },
+       { "XK_Button_2", XK_Button_2 },
+       { "XK_Button_3", XK_Button_3 },
+       { "XK_Scroll_Up", XK_Scroll_Up },
+       { "XK_Scroll_Down", XK_Scroll_Down },
+#include "shuttle_keys.h"
+       { NULL, 0 }
+};
+
+KeySym KeySymMapping::to_keysym(const char *str)
+{
+       for( KeySymMapping *ksp = &key_sym_mapping[0]; ksp->str; ++ksp )
+               if( !strcmp(str, ksp->str) ) return ksp->sym;
+       return 0;
+}
+
+const char *KeySymMapping::to_string(KeySym ks)
+{
+       for( KeySymMapping *ksp = &key_sym_mapping[0]; ksp->sym; ++ksp )
+               if( ksp->sym == ks ) return ksp->str;
+       return 0;
+}
+
+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, &regex, emsg, sizeof(emsg));
+               fprintf(stderr, "regerror: %s\n", emsg);
+       }
+}
+TransName::~TransName()
+{
+       delete [] name;
+       regfree(&regex);
+}
+
+void Translation::init(int def)
+{
+       is_default = def;
+       is_key = 0;
+       first_release_stroke = 0;
+       pressed = 0;
+       released = 0;
+       keysym_down = 0;
+}
+
+Translation::Translation(Shuttle *shuttle)
+ : modifiers(this)
+{ // initial default translation
+       init(1);
+       this->shuttle = shuttle;
+       this->name = cstrdup("Default");
+       names.append(new TransName(FOCUS_DEFAULT, name, ""));
+       key_down[K6].add_stroke(XK_Button_1, 1);
+       key_up[K6].add_stroke(XK_Button_1, 0);
+       key_down[K7].add_stroke(XK_Button_2, 1);
+       key_up[K7].add_stroke(XK_Button_2, 0);
+       key_down[K8].add_stroke(XK_Button_3, 1);
+       key_up[K8].add_stroke(XK_Button_3, 0);
+       jog[JL].add_stroke(XK_Scroll_Up, 1);
+       jog[JL].add_stroke(XK_Scroll_Up, 0);
+       jog[JR].add_stroke(XK_Scroll_Down, 0);
+       jog[JR].add_stroke(XK_Scroll_Down, 1);
+}
+
+Translation::Translation(Shuttle *shuttle, const char *name)
+ : modifiers(this)
+{
+       init(0);
+       this->shuttle = shuttle;
+       this->name = cstrdup(name);
+}
+
+Translation::~Translation()
+{
+       delete [] name;
+}
+
+void Translation::clear()
+{
+       names.remove_all_objects();
+       init(0);
+       for( int i=0; i<NUM_KEYS; ++i ) key_down[i].clear();
+       for( int i=0; i<NUM_KEYS; ++i ) key_up[i].clear();
+       for( int i=0; i<NUM_SHUTTLES; ++i ) shuttles[i].clear();
+       for( int i=0; i<NUM_JOGS; ++i ) jog[i].clear();
+}
+
+void Translation::append_stroke(KeySym sym, int press)
+{
+       Stroke *s = pressed_strokes->append();
+       s->keysym = sym;
+       s->press = press;
+}
+
+void Translation::add_keysym(KeySym sym, int press_release)
+{
+//printf("add_keysym(0x%x, %d)\n", (int)sym, press_release);
+       switch( press_release ) {
+       case PRESS:
+               append_stroke(sym, 1);
+               modifiers.mark_as_down(sym, 0);
+               break;
+       case RELEASE:
+               append_stroke(sym, 0);
+               modifiers.mark_as_up(sym);
+               break;
+       case HOLD:
+               append_stroke(sym, 1);
+               modifiers.mark_as_down(sym, 1);
+               break;
+       case PRESS_RELEASE:
+       default:
+               if( first_release_stroke ) {
+                       modifiers.re_press();
+                       first_release_stroke = 0;
+               }
+               if( keysym_down ) {
+                       append_stroke(keysym_down, 0);
+               }
+               append_stroke(sym, 1);
+               keysym_down = sym;
+               break;
+       }
+}
+
+void Translation::add_release(int all_keys)
+{
+//printf("add_release(%d)\n", all_keys);
+       modifiers.release(all_keys);
+       if( !all_keys ) {
+               pressed_strokes = released_strokes;
+       }
+       if( keysym_down ) {
+               append_stroke(keysym_down, 0);
+               keysym_down = 0;
+       }
+       first_release_stroke = 1;
+}
+
+void Translation::add_keystroke(const char *keySymName, int press_release)
+{
+       KeySym sym;
+
+       if( is_key && !strncmp(keySymName, "RELEASE", 8) ) {
+               add_release(0);
+               return;
+       }
+       sym = KeySymMapping::to_keysym(keySymName);
+       if( sym != 0 ) {
+               add_keysym(sym, press_release);
+       }
+       else
+               fprintf(stderr, "unrecognized KeySym: %s\n", keySymName);
+}
+
+void Translation::add_string(const char *str)
+{
+       while( str && *str ) {
+               if( *str >= ' ' && *str <= '~' )
+                       add_keysym((KeySym)(*str), PRESS_RELEASE);
+               ++str;
+       }
+}
+
+int Translation::start_line(const char *key)
+{
+       pressed_strokes = 0;
+       released_strokes = 0;
+       pressed = released = 0;
+       is_key = 0;
+       if( !strcasecmp("JL", key) ) {
+               pressed = &jog[0];
+       }
+       else if( !strcasecmp("JR", key) ) {
+               pressed = &jog[1];
+       }
+       else {
+               char c = 0;  int k = -1, n = 0;
+               if( sscanf(key, "%c%d%n", &c, &k, &n) != 2 ) return 1;
+               switch( c ) {
+               case 'K': case 'k':
+                       --k;
+                       if( k >= K1 && k <= K15 ) {
+                               pressed = &key_down[k];
+                               released = &key_up[k];
+                               is_key = 1;
+                       }
+                       break;
+               case 'S': case 's':
+                       if( k >= S_7 && k <= S7 ) {
+                               pressed = &shuttles[k-S_7];
+                       }
+                       break;
+               }
+               if( !pressed ) {
+                       fprintf(stderr, "bad key name: [%s]%s\n", name, key);
+                       return 1;
+               }
+               if( pressed->first ) {
+                       fprintf(stderr, "dupl key name: [%s]%s\n", name, key);
+                       return 1;
+               }
+       }
+       pressed_strokes = pressed;
+       released_strokes = released;
+       return 0;
+}
+
+void Translation::print_stroke(Stroke *s)
+{
+       if( !s ) return;
+       const char *cp = KeySymMapping::to_string(s->keysym);
+       if( !cp ) { printf("0x%x", (int)s->keysym); cp = "???"; }
+       printf("%s/%c ", cp, s->press ? 'D' : 'U');
+}
+
+void Translation::print_strokes(const char *name, const char *up_dn, Strokes *strokes)
+{
+       printf("%s[%s]: ", name, up_dn);
+       for( Stroke *s=strokes->first; s; s=s->next )
+               print_stroke(s);
+       printf("\n");
+}
+
+void Translation::finish_line()
+{
+//printf("finish_line()\n");
+       if( is_key ) {
+               add_release(0);
+       }
+       add_release(1);
+}
+
+void Translation::print_line(const char *key)
+{
+       if( is_key ) {
+               print_strokes(key, "D", pressed_strokes);
+               print_strokes(key, "U", released_strokes);
+       }
+       else {
+               print_strokes(key, "", pressed_strokes);
+       }
+       printf("\n");
+}
+
+// press values in Modifiers:
+// PRESS -> down
+// HOLD -> held
+// PRESS_RELEASE -> released, but to be re-pressed if necessary
+// RELEASE -> up
+
+void Modifiers::mark_as_down(KeySym sym, int hold)
+{
+       Modifiers &modifiers = *this;
+       for( int i=0; i<size(); ++i ) {
+               Stroke &s = modifiers[i];
+               if( s.keysym == sym ) {
+                       s.press = hold ? HOLD : PRESS;
+                       return;
+               }
+       }
+       Stroke &s = append();
+       s.keysym = sym;
+       s.press = hold ? HOLD : PRESS;
+}
+
+void Modifiers::mark_as_up(KeySym sym)
+{
+       Modifiers &modifiers = *this;
+       for( int i=0; i<size(); ++i ) {
+               Stroke &s = modifiers[i];
+               if( s.keysym == sym ) {
+                       s.press = RELEASE;
+                       return;
+               }
+       }
+}
+
+void Modifiers::release(int allkeys)
+{
+       Modifiers &modifiers = *this;
+       for( int i=0; i<size(); ++i ) {
+               Stroke &s = modifiers[i];
+               if( s.press == PRESS ) {
+                       trans->append_stroke(s.keysym, 0);
+                       s.press = PRESS_RELEASE;
+               }
+               else if( allkeys && s.press == HOLD ) {
+                       trans->append_stroke(s.keysym, 0);
+                       s.press = RELEASE;
+               }
+       }
+}
+
+void Modifiers::re_press()
+{
+       Modifiers &modifiers = *this;
+       for( int i=0; i<size(); ++i ) {
+               Stroke &s = modifiers[i];
+               if( s.press == PRESS_RELEASE ) {
+                       trans->append_stroke(s.keysym, 1);
+                       s.press = PRESS;
+               }
+       }
+}
+
+
+Shuttle::Shuttle(MWindow *mwindow)
+ : Thread(0, 0, 0)
+{
+       this->mwindow = mwindow;
+
+       fd = -1;
+       wdw = 0;
+       win = 0;
+       msk = 0;
+       rx = ry = 0;
+       wx = wy = 0;
+       jogvalue = 0xffff;
+       shuttlevalue = 0xffff;
+       last_shuttle.tv_sec = 0;
+       last_shuttle.tv_usec = 0;
+       need_synthetic_shuttle = 0;
+       dev_name = 0;
+
+       done = -1;
+       failed = 0;
+       first_time = 1;
+       tr = 0;
+       last_translation = 0;
+       last_focused = 0;
+
+       default_translation = new Translation(this);
+       config_path = 0;
+       config_mtime = 0;
+       ev.type = ~0;
+}
+
+Shuttle::~Shuttle()
+{
+       stop();
+       delete default_translation;
+       delete [] config_path;
+}
+
+int Shuttle::send_button(unsigned int button, int press)
+{
+       if( debug )
+               printf("btn: %u %d\n", button, press);
+       XButtonEvent *b = new XButtonEvent();
+       memset(b, 0, sizeof(*b));
+       b->type = press ? ButtonPress : ButtonRelease;
+       b->time = milliTimeClock();
+       b->display = wdw->top_level->display;
+       b->root = wdw->top_level->rootwin;
+       b->window = win;
+       b->x_root = rx;
+       b->y_root = ry;
+       b->x = wx;
+       b->y = wy;
+       b->state = msk;
+       b->button = button;
+       b->same_screen = 1;
+       wdw->top_level->put_event((XEvent *) b);
+       return 0;
+}
+int Shuttle::send_key(KeySym keysym, int press)
+{
+       KeyCode keycode = XKeysymToKeycode(wdw->top_level->display, keysym);
+       if( debug )
+               printf("key: %04x %d\n", (unsigned)keycode, press);
+       XKeyEvent *k = new XKeyEvent();
+       memset(k, 0, sizeof(*k));
+       k->type = press ? KeyPress : KeyRelease;
+       k->time = milliTimeClock();
+       k->display = wdw->top_level->display;
+       k->root = wdw->top_level->rootwin;
+       k->window = win;
+       k->x_root = rx;
+       k->y_root = ry;
+       k->x = wx;
+       k->y = wy;
+       k->state = msk;
+       k->keycode = keycode;
+       k->same_screen = 1;
+       wdw->top_level->put_event((XEvent *) k);
+       return 0;
+}
+
+int Shuttle::send_keysym(KeySym 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);
+}
+
+
+static Stroke *fetch_stroke(Translation *translation, int kjs, int index)
+{
+       Stroke *ret = 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;
+               }
+       }
+       return ret;
+}
+
+void Shuttle::send_stroke_sequence(int kjs, int index)
+{
+       if( !wdw ) return;
+       Stroke *s = fetch_stroke(tr, kjs, index);
+       if( !s ) s = fetch_stroke(default_translation, kjs, index);
+       while( s ) {
+               send_keysym(s->keysym, s->press);
+               s = s->next;
+       }
+}
+
+void Shuttle::key(unsigned short code, unsigned int value)
+{
+       code -= EVENT_CODE_KEY1;
+       if( code >= NUM_KEYS ) {
+               fprintf(stderr, "key(%d, %d) out of range\n", code + EVENT_CODE_KEY1, value);
+               return;
+       }
+       send_stroke_sequence(value ? KJS_KEY_DOWN : KJS_KEY_UP, code);
+}
+
+
+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 ) {
+               shuttlevalue = value;
+               send_stroke_sequence(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 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;
+               while( jogvalue != value ) {
+                       // driver fails to send an event when jogvalue == 0
+                       if( jogvalue != 0 ) {
+       send_stroke_sequence(KJS_JOG, direction > 0 ? 1 : 0);
+                       }
+                       jogvalue = (jogvalue + direction) & 0xff;
+               }
+       }
+       jogvalue = value;
+}
+
+void Shuttle::jogshuttle(unsigned short code, unsigned int value)
+{
+       switch( code ) {
+       case EVENT_CODE_JOG:
+               jog(value);
+               break;
+       case EVENT_CODE_SHUTTLE:
+               shuttle(value);
+               break;
+       default:
+               fprintf(stderr, "jogshuttle(%d, %d) invalid code\n", code, value);
+               break;
+       }
+}
+
+const char *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;
+}
+
+void Shuttle::start(const char *dev_name)
+{
+       this->dev_name = dev_name;
+       first_time = 1;
+       done = 0;
+       Thread::start();
+}
+
+void Shuttle::stop()
+{
+       if( running() && !done ) {
+               done = 1;
+               cancel();
+               join();
+       }
+}
+
+
+int Shuttle::get_focused_window_translation()
+{
+       MWindowGUI *gui = mwindow->gui;
+       Display *dpy = gui->display;
+       Window focus = 0;
+       int ret = 0, revert = 0;
+       char win_title[BCTEXTLEN];
+       gui->lock_window("Shuttle::get_focused_window_translation");
+       XGetInputFocus(dpy, &focus, &revert);
+       if( last_focused != focus ) {
+               last_focused = focus;
+               Atom prop = XInternAtom(dpy, "WM_NAME", False);
+               Atom type;  int form;
+               unsigned long remain, len;
+               unsigned char *list;
+               if( XGetWindowProperty(dpy, focus, prop, 0, sizeof(win_title)-1, False,
+                       AnyPropertyType, &type, &form, &len, &remain, &list) == Success ) {
+                       len = len*(form/8) - remain;
+                       memcpy(win_title, list, len);
+                       win_title[len] = 0;
+                       XFree(list);
+                       if( debug )
+                               printf("new focus: %08x\n", (unsigned)focus);
+               }
+               else {
+                       last_focused = 0;
+                       fprintf(stderr, "XGetWindowProperty failed for window 0x%x\n",
+                                       (int)focus);
+                       ret = 1;
+               }
+       }
+       gui->unlock_window();
+       if( ret ) return -1;
+
+       this->wdw = 0;  this->win = 0;
+       this->wx = 0;   this->wy = 0;
+       this->rx = 0;   this->ry = 0;
+       this->msk = 0;
+       BC_WindowBase *wdw = 0;
+       int cin = -1;
+       if( (wdw=mwindow->gui) && wdw->win == focus )
+               cin = FOCUS_MWINDOW;
+       else if( (wdw=mwindow->awindow->gui) && wdw->win == focus )
+               cin = FOCUS_AWINDOW;
+       else if( (wdw=mwindow->cwindow->gui) && wdw->win == focus )
+               cin = FOCUS_CWINDOW;
+       else if( (wdw=mwindow->gui->mainmenu->load_file->thread->window) &&
+                wdw->win == focus )
+               cin = FOCUS_LOAD;
+       else {
+               int i = mwindow->vwindows.size();
+               while( --i >= 0 ) {
+                       VWindow *vwdw =  mwindow->vwindows[i];
+                       if( !vwdw->is_running() ) continue;
+                       if( (wdw=vwdw->gui) && wdw->win == focus ) {
+                               cin = FOCUS_VIEWER;  break;
+                       }
+               }
+       }
+       if( cin < 0 ) return -1;
+       Window root = 0, child = 0;
+       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,
+                       &root_x, &root_y, &win_x, &win_y, &mask) ) {
+               if( !child ) {
+                       if( wdw->active_menubar )
+                               child = wdw->active_menubar->win;
+                       else if( wdw->active_popup_menu )
+                               child = wdw->active_popup_menu->win;
+                       else if( wdw->active_subwindow )
+                               child = wdw->active_subwindow->win;
+               }
+               if( child )
+                       XGetGeometry(wdw->display, child, &root, &x, &y,
+                               &width, &height, &border_width, &depth);
+       }
+       wdw->unlock_window();
+       if( !child || !wdw->match_window(child) ) return -1;
+// success
+       this->wdw = wdw;
+       this->win = child;
+       this->msk = mask;
+       this->rx = root_x;
+       this->ry = root_y;
+       this->wx = win_x - x;
+       this->wy = win_y - y;
+       for( tr=translations.first; tr; tr=tr->next ) {
+               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;
+               }
+       }
+       tr = default_translation;
+       return 0;
+}
+
+void Shuttle::handle_event()
+{
+       if( read_config_file() > 0 ) {
+               done = 1;
+               return;
+       }
+       if( get_focused_window_translation() < 0 )
+               return;
+       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);
+       switch( ev.type ) {
+       case EVENT_TYPE_DONE:
+       case EVENT_TYPE_ACTIVE_KEY:
+               break;
+       case EVENT_TYPE_KEY:
+               key(ev.code, ev.value);
+               break;
+       case EVENT_TYPE_JOGSHUTTLE:
+               jogshuttle(ev.code, ev.value);
+               break;
+       default:
+               fprintf(stderr, "handle_event() invalid type code\n");
+               break;
+       }
+}
+
+void Shuttle::run()
+{
+       for( enable_cancel(); !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
+                       first_time = 0;
+                       while( !done ) {
+                               int ret = read(fd, &ev, sizeof(ev));
+                               if( done ) break;
+                               if( ret != sizeof(ev) ) {
+                                       if( ret < 0 ) { perror("read event"); break; }
+                                       fprintf(stderr, "bad read: %d\n", ret);
+                                       break;
+                               }
+                               handle_event();
+                       }
+               }
+               else
+                       perror( "evgrab ioctl" );
+               close(fd);
+       }
+       done = 2;
+}
+
+int Shuttle::read_config_file()
+{
+       if( !config_path ) {
+               const char *env;
+               config_path = (env=getenv("SHUTTLE_CONFIG_FILE")) != 0 ? cstrdup(env) :
+                       (env=getenv("HOME")) != 0 ? cstrcat(2, env, "/.shuttlerc") : 0;
+               if( !config_path ) { fprintf(stderr, "no config file\n");  return 1; }
+       }
+       struct stat st;
+       if( stat(config_path, &st) ) {
+               perror(config_path);
+               char shuttlerc[BCTEXTLEN];
+               snprintf(shuttlerc, sizeof(shuttlerc), "%s/shuttlerc",
+                               File::get_cindat_path());
+               if( stat(shuttlerc, &st) ) {
+                       perror(shuttlerc);
+                       return 1;
+               }
+               delete [] config_path;
+               config_path = cstrdup(shuttlerc);
+       }
+       if( config_mtime > 0 &&
+           config_mtime == st.st_mtime ) return 0;
+       FILE *fp = fopen(config_path, "r");
+       if( !fp ) {
+               perror(config_path);
+               return 1;
+       }
+
+       config_mtime = st.st_mtime;
+       translations.clear();
+       debug = 0;
+#define ws(ch) (ch==' ' || ch=='\t')
+       char line[BCTEXTLEN], *cp;
+       Translation *trans = 0;
+       int no = 0;
+       int ret = fgets(cp=line,sizeof(line),fp) ? 0 : -1;
+       if( !ret ) ++no;
+// lines
+       while( !ret ) {
+// translation names
+               while( !ret && *cp == '[' ) {
+                       char *name = ++cp;
+                       while( *cp && *cp != ']' ) ++cp;
+                       *cp++ = 0;
+                       if( !name || !*name ) { ret = 1;  break; }
+                       int cin = -1;
+                       if( !strcasecmp("default", name) )
+                               cin = FOCUS_DEFAULT;
+                       else if( !strcasecmp("cinelerra", name) )
+                               cin = FOCUS_MWINDOW;
+                       else if( !strcasecmp("resources", name) )
+                               cin = FOCUS_AWINDOW;
+                       else if( !strcasecmp("composer", name) )
+                               cin = FOCUS_CWINDOW;
+                       else if( !strcasecmp("viewer", name) )
+                               cin = FOCUS_VIEWER;
+                       else if( !strcasecmp("load", name) )
+                               cin = FOCUS_LOAD;
+                       else {
+                               fprintf(stderr, "unknown focus target window: %s\n",
+                                        name);
+                               ret = 1;  break;
+                       }
+                       if( !trans ) {
+                               if( cin == FOCUS_DEFAULT ) {
+                                       trans = default_translation;
+                                       trans->clear();
+                               }
+                               else {
+                                       trans = new Translation(this, name);
+                               }
+                       }
+                       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",
+                                       trans->names.last()->name);
+                               ret = 1;  break;
+                       }
+                       ++no;
+               }
+               if( ret ) break;
+               if( debug && trans ) {
+                       printf("------------------------\n");
+                       TransNames &names = trans->names;
+                       for( int i=0; i<names.size(); ++i ) {
+                               TransName *tp = names[i];
+                               printf("[%s] # %d\n\n", tp->name, tp->cin);
+                       }
+               }
+// rules lines: "tok <stroke list>\n"
+               while( !ret && *cp != '[' ) {
+                       const char *key = 0, *tok = 0;
+                       while( ws(*cp) ) ++cp;
+                       if( !*cp || *cp == '\n' || *cp == '#' ) goto skip;
+                       tok = cp;
+                       while( *cp && !ws(*cp) && *cp != '\n' ) ++cp;
+                       *cp++ = 0;
+                       if( !strcmp(tok, "DEBUG") ) {
+                               debug = 1;  goto skip;
+                       }
+                       key = tok;
+                       if( !trans ) {
+                               fprintf(stderr, "no translation section defining key: %s\n", key);
+                               ret = 1;  break;
+                       }
+       
+                       ret = trans->start_line(key);
+                       while( !ret && *cp && *cp != '\n' ) {
+                               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];
+                                       }
+                                       *cp++ = 0;
+                                       trans->add_string(tok);
+                                       continue;
+                               }
+                               tok = cp;
+                               while( *cp && !ws(*cp) && *cp!='/' && *cp != '\n' ) ++cp;
+                               int dhu = PRESS_RELEASE;
+                               if( *cp == '/' ) {
+                                       *cp++ = 0;
+                                       switch( *cp ) {
+                                       case 'D':  dhu = PRESS;    break;
+                                       case 'H':  dhu = HOLD;     break;
+                                       case 'U':  dhu = RELEASE;  break;
+                                       default:
+                                               fprintf(stderr, "invalid up/down modifier [%s]%s: '%c'\n",
+                                                       trans->name, tok, *cp);
+                                               ret = 1;  break;
+                                       }
+                                       ++cp;
+                               }
+                               else
+                                       *cp++ = 0;
+                               trans->add_keystroke(tok, dhu);
+                       }
+                       if( ret ) break;
+                       trans->finish_line();
+                       if( debug )
+                               trans->print_line(key);
+               skip:   ret = fgets(cp=line,sizeof(line),fp) ? 0 : -1;
+                       if( !ret ) ++no;
+               }
+               if( trans ) {
+                       if( trans != default_translation )
+                               translations.append(trans);
+                       trans = 0;
+               }
+       }
+       if( ret > 0 )
+               fprintf(stderr, "shuttle config err file: %s, line:%d\n",
+                       config_path, no);
+
+       fclose(fp);
+       return ret;
+}
+#endif
diff --git a/cinelerra-5.1/cinelerra/shuttle.h b/cinelerra-5.1/cinelerra/shuttle.h
new file mode 100644 (file)
index 0000000..532a560
--- /dev/null
@@ -0,0 +1,248 @@
+#ifndef __SHUTTLE_H__
+#define __SHUTTLE_H__
+
+#include "arraylist.h"
+#include "bcwindowbase.inc"
+#include "linklist.h"
+#include "shuttle.inc"
+#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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <linux/input.h>
+#include <linux/input-event-codes.h>
+#include <sys/types.h>
+#include <regex.h>
+
+
+// Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact)
+// reworked 2019 for cinelerra-gg by William Morrow (aka goodguy)
+
+// delay in ms before processing each XTest event
+// CurrentTime means no delay
+#define DELAY CurrentTime
+
+// protocol for events from the shuttlepro HUD device
+//
+// ev.type values:
+#define EVENT_TYPE_DONE 0
+#define EVENT_TYPE_KEY 1
+#define EVENT_TYPE_JOGSHUTTLE 2
+#define EVENT_TYPE_ACTIVE_KEY 4
+
+// ev.code when ev.type == KEY
+#define EVENT_CODE_KEY1 256
+// KEY2 257, etc...
+
+enum { K1=0,K2,K3,K4,K5,K6,K7,K8,K9,K10,K11,K12,K13,K14,K15, };
+enum { S_7=-7,S_6,S_5,S_4,S_3,S_2,S_1,S0,S1,S2,S3,S4,S5,S6,S7, };
+enum { JL=0,JR };
+
+// ev.value when ev.type == KEY
+// 1 -> PRESS; 0 -> RELEASE
+
+// ev.code when ev.type == JOGSHUTTLE
+#define EVENT_CODE_JOG 7
+#define EVENT_CODE_SHUTTLE 8
+
+// ev.value when ev.code == JOG
+// 8 bit value changing by one for each jog step
+
+// ev.value when ev.code == SHUTTLE
+// -7 .. 7 encoding shuttle position
+
+// we define these as extra KeySyms to represent mouse events
+#define XK_Button_0 0x2000000 // just an offset, not a real button
+#define XK_Button_1 0x2000001
+#define XK_Button_2 0x2000002
+#define XK_Button_3 0x2000003
+#define XK_Scroll_Up 0x2000004
+#define XK_Scroll_Down 0x2000005
+
+#define PRESS 1
+#define RELEASE 2
+#define PRESS_RELEASE 3
+#define HOLD 4
+
+#define NUM_KEYS 15
+#define NUM_SHUTTLES 15
+#define NUM_JOGS 2
+
+#define KJS_KEY_DOWN 1
+#define KJS_KEY_UP 2
+#define KJS_SHUTTLE 3
+#define KJS_JOG 4
+
+// cinelerra window input targets
+#define FOCUS_DEFAULT 0
+#define FOCUS_MWINDOW 1
+#define FOCUS_AWINDOW 2
+#define FOCUS_CWINDOW 3
+#define FOCUS_VIEWER  4
+#define FOCUS_LOAD    5
+
+class KeySymMapping {
+public:
+       static KeySym to_keysym(const char *str);
+       static const char *to_string(KeySym ks);
+       static KeySymMapping key_sym_mapping[];
+       const char *str;
+       KeySym sym;
+};
+
+class Stroke : public ListItem<Stroke>
+{
+public:
+       KeySym keysym;
+       int press; // 1:press, 0:release
+};
+
+class Strokes : public List<Stroke>
+{
+public:
+       Strokes() {}
+       ~Strokes() {}
+       void clear() { while( last ) delete last; }
+       void add_stroke(KeySym keysym, int press=1) {
+               Stroke *s = append();
+               s->keysym = keysym; s->press = press;
+       }
+};
+
+class Modifiers : public ArrayList<Stroke>
+{
+       Translation *trans;
+public:
+       Modifiers(Translation *trans) { this->trans = trans; }
+       ~Modifiers() {}
+
+       void mark_as_down(KeySym sym, int hold);
+       void mark_as_up(KeySym sym);
+       void release(int allkeys);
+       void re_press();
+};
+
+class TransName
+{
+public:
+       int cin, err;
+       const char *name;
+       regex_t regex;
+
+       TransName(int cin, const char *nm, const char *re);
+       ~TransName();
+};
+
+class TransNames : public ArrayList<TransName *>
+{
+public:
+       TransNames() {}
+       ~TransNames() { remove_all_objects(); }
+};
+
+class Translation : public ListItem<Translation>
+{
+public:
+       Translation(Shuttle *shuttle);
+       Translation(Shuttle *shuttle, const char *name);
+       ~Translation();
+       void init(int def);
+       void clear();
+       void append_stroke(KeySym sym, int press);
+       void add_release(int all_keys);
+       void add_keystroke(const char *keySymName, int press_release);
+       void add_keysym(KeySym sym, int press_release);
+       void add_string(const char *str);
+       int start_line(const char *key);
+       void print_strokes(const char *name, const char *up_dn, Strokes *strokes);
+       void print_stroke(Stroke *s);
+       void finish_line();
+       void print_line(const char *key);
+
+       Shuttle *shuttle;
+       TransNames names;
+       const char *name;
+       int is_default, is_key;
+       int first_release_stroke;
+       Strokes *pressed, *released;
+       Strokes *pressed_strokes, *released_strokes;
+       KeySym keysym_down;
+
+       Strokes key_down[NUM_KEYS];
+       Strokes key_up[NUM_KEYS];
+       Strokes shuttles[NUM_SHUTTLES];
+       Strokes jog[NUM_JOGS];
+       Modifiers modifiers;
+};
+
+class Translations : public List<Translation>
+{
+public:
+       Translations() {}
+       ~Translations() {}
+       void clear() { while( last ) delete last; }
+};
+
+class Shuttle : public Thread
+{
+       int fd;
+       unsigned short jogvalue;
+       int shuttlevalue;
+       struct timeval last_shuttle;
+       int need_synthetic_shuttle;
+       const char *dev_name;
+       Translation *default_translation;
+       Translations translations;
+public:
+       Shuttle(MWindow *mwindow);
+       ~Shuttle();
+
+       int send_button(unsigned int button, int press);
+       int send_key(KeySym keysym, int press);
+       int send_keysym(KeySym keysym, int press);
+       void send_stroke_sequence(int kjs, int index);
+       void key(unsigned short code, unsigned int value);
+       void shuttle(int value);
+       void jog(unsigned int value);
+       void jogshuttle(unsigned short code, unsigned int value);
+       void start(const char *dev_name);
+       void stop();
+       void handle_event();
+       int get_focused_window_translation();
+       static const char *probe();
+       void run();
+       int read_config_file();
+
+       int done;
+       int failed;
+       int first_time;
+       int debug;
+
+       MWindow *mwindow;
+       Translation *tr, *last_translation;
+       BC_WindowBase *wdw;
+       Window win;
+       unsigned msk;
+       int rx, ry, wx, wy;
+       Window last_focused;
+
+       const char *config_path;
+       time_t config_mtime;
+       input_event ev;
+};
+
+#endif
diff --git a/cinelerra-5.1/cinelerra/shuttle.inc b/cinelerra-5.1/cinelerra/shuttle.inc
new file mode 100644 (file)
index 0000000..5919e9d
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __SHUTTLE_INC__
+#define __SHUTTLE_INC__
+
+class KeySymMapping;
+class Stroke;
+class Strokes;
+class Modifiers;
+class Translation;
+class Translations;
+class Shuttle;
+
+#endif
diff --git a/cinelerra-5.1/cinelerra/shuttle.sed b/cinelerra-5.1/cinelerra/shuttle.sed
new file mode 100644 (file)
index 0000000..1996b8b
--- /dev/null
@@ -0,0 +1,5 @@
+/^\#ifdef/p
+/^\#endif/p
+/^\#define/!d
+s/^\#define //
+s/^\([^[:space:]]*\).*$/{ "\1", \1 }, /
index 2ec6921..84081a0 100644 (file)
@@ -53,6 +53,7 @@ CHECK_WITH([booby],[window lock trace booby trap],[BOOBY],[no])
 CHECK_WITH([libzmpeg],[build libzmpeg],[LIBZMPEG],[yes])
 CHECK_WITH([commercial],[enable commercial capture],[COMMERCIAL],[yes])
 CHECK_WITH([thirdparty],[use thirdparty build],[CIN_3RDPARTY],[yes])
+CHECK_WITH([shuttle],[shuttle device],[SHUTTLE],[yes])
 
 if test "x$WANT_LV2" != "xno"; then
   GTK2_LIBS=`pkg-config --libs gtk+-2.0`
@@ -807,7 +808,7 @@ if test "x$WANT_CIN_3RDPARTY" != "xno"; then
 fi
 for v in GL XFT XXF86VM OSS ALSA FIREWIRE DV DVB \
         VIDEO4LINUX2 ESOUND PACTL OPENEXR LV2 \
-        COMMERCIAL LIBZMPEG; do
+        COMMERCIAL LIBZMPEG SHUTTLE; do
   eval vv="\$WANT_$v"
   if test "x$vv" != "xno"; then
     CFG_CFLAGS+=" -DHAVE_$v"
@@ -833,6 +834,7 @@ echo "  using: with-noelision = $WANT_NOELISION"
 echo "  using: with-booby = $WANT_BOOBY"
 echo "  using: with-libzmpeg = $WANT_LIBZMPEG"
 echo "  using: with-commerical = $WANT_COMMERCIAL"
+echo "  using: with-shuttle = $WANT_SHUTTLE"
 echo ""
 echo "  using: thirdparty build = $WANT_CIN_3RDPARTY"
 echo "  using: single-user  = $WANT_CINBIN_BUILD"
index 84cb089..b512820 100644 (file)
@@ -173,6 +173,7 @@ public:
        friend class BC_Tumbler;
        friend class BC_Window;
        friend class BC_WindowEvents;
+       friend class Shuttle;
 
 // Main loop
        int run_window();
index e3f6880..046e7dd 100644 (file)
@@ -25,6 +25,8 @@
 class BC_ActiveBitmaps;
 class BC_WindowBase;
 class BC_WindowList;
+class Shuttle;
+
 #define BCTEXTLEN 1024
 #define BCSTRLEN 64
 #define KEYPRESSLEN 6
diff --git a/cinelerra-5.1/plugins/theme_blond/data/ff_audio.png b/cinelerra-5.1/plugins/theme_blond/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_blond/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_blond/data/ff_video.png b/cinelerra-5.1/plugins/theme_blond/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_blond/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_blond_cv/data/ff_audio.png b/cinelerra-5.1/plugins/theme_blond_cv/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_blond_cv/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_blond_cv/data/ff_video.png b/cinelerra-5.1/plugins/theme_blond_cv/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_blond_cv/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_blue/data/ff_audio.png b/cinelerra-5.1/plugins/theme_blue/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_blue/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_blue/data/ff_video.png b/cinelerra-5.1/plugins/theme_blue/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_blue/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_blue_dot/data/ff_audio.png b/cinelerra-5.1/plugins/theme_blue_dot/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_blue_dot/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_blue_dot/data/ff_video.png b/cinelerra-5.1/plugins/theme_blue_dot/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_blue_dot/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_bright/data/ff_audio.png b/cinelerra-5.1/plugins/theme_bright/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_bright/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_bright/data/ff_video.png b/cinelerra-5.1/plugins/theme_bright/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_bright/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_hulk/data/ff_audio.png b/cinelerra-5.1/plugins/theme_hulk/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_hulk/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_hulk/data/ff_video.png b/cinelerra-5.1/plugins/theme_hulk/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_hulk/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_pinklady/data/ff_audio.png b/cinelerra-5.1/plugins/theme_pinklady/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_pinklady/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_pinklady/data/ff_video.png b/cinelerra-5.1/plugins/theme_pinklady/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_pinklady/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_suv/data/ff_audio.png b/cinelerra-5.1/plugins/theme_suv/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_suv/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_suv/data/ff_video.png b/cinelerra-5.1/plugins/theme_suv/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_suv/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_unflat/data/ff_audio.png b/cinelerra-5.1/plugins/theme_unflat/data/ff_audio.png
deleted file mode 100644 (file)
index c62e407..0000000
Binary files a/cinelerra-5.1/plugins/theme_unflat/data/ff_audio.png and /dev/null differ
diff --git a/cinelerra-5.1/plugins/theme_unflat/data/ff_video.png b/cinelerra-5.1/plugins/theme_unflat/data/ff_video.png
deleted file mode 100644 (file)
index 73c231b..0000000
Binary files a/cinelerra-5.1/plugins/theme_unflat/data/ff_video.png and /dev/null differ
diff --git a/cinelerra-5.1/shuttlerc b/cinelerra-5.1/shuttlerc
new file mode 100644 (file)
index 0000000..a41ba3c
--- /dev/null
@@ -0,0 +1,92 @@
+
+# uncomment to enable diagnostics
+#DEBUG
+
+# redefine default, use
+# also used for resources,load windows
+[Default]
+[Resources]
+[Load]
+ K5 XK_Home
+ K6 XK_Button_1
+ K7 XK_Button_2
+ K8 XK_Button_3
+ K9 XK_End
+ JL XK_Scroll_Up
+ JR XK_Scroll_Down
+
+[Cinelerra]
+
+ K5 XK_KP_0     # Stop
+ K9 XK_KP_3     # Play
+ K12 XK_Home    # Beginning
+ K13 XK_End     # End
+ K14 "["        # Toggle in
+ K15 "]"        # Toggle out
+
+ K1 "i"                # Pro Only - Clip
+ K2 "x"                # Pro Only - Cut
+ K3 "c"                # Pro Only - Copy
+ K4 "v"                # Pro Only - Paste
+
+ S-3 XK_KP_Add  # Fast reverse
+ S-2 XK_KP_6    # Play reverse
+ S-1 XK_KP_5    # Slow reverse
+ S0 XK_KP_0     # Stop
+ S1 XK_KP_2     # Slow forward
+ S2 XK_KP_3     # Play forward
+ S3 XK_KP_Enter # Fast forward
+ JL XK_KP_4     # Frame reverse
+ JR XK_KP_1     # Frame forward
+
+[Composer]
+
+ K5 XK_KP_0     # Stop
+ K9 XK_KP_3     # Play
+ K12 XK_Home    # Beginning
+ K13 XK_End     # End
+ K14 "["        # Toggle in
+ K15 "]"        # Toggle out
+
+ K1 "i"                # Pro Only - Clip
+ K2 "x"                # Pro Only - Cut
+ K3 "c"                # Pro Only - Copy
+ K4 "v"                # Pro Only - Paste
+
+ S-3 XK_KP_Add  # Fast reverse
+ S-2 XK_KP_6    # Play reverse
+ S-1 XK_KP_5    # Slow reverse
+ S0 XK_KP_0     # Stop
+ S1 XK_KP_2     # Slow forward
+ S2 XK_KP_3     # Play forward
+ S3 XK_KP_Enter # Fast forward
+ JL XK_KP_4     # Frame reverse
+ JR XK_KP_1     # Frame forward
+
+[Viewer]
+
+ K5 "v"                # Splice (Viewer only - button is on Xpress)
+ K9 "b"                # Overwrite (Viewer only - button is on Xpress)
+ K12 XK_Home    # Beginning
+ K13 XK_End     # End
+ K14 "["        # Toggle in
+ K15 "]"        # Toggle out
+
+ K1 "i"                # Pro Only - Clip
+ K2 XK_Home    # Pro Only - Beginning 
+ K3 "c"                # Pro Only - Copy
+ K4 XK_End     # Pro Only - End
+
+ S-3 XK_KP_Add  # Fast reverse
+ S-2 XK_KP_6    # Play reverse
+ S-1 XK_KP_5    # Slow reverse
+ S0 XK_KP_0     # Stop
+ S1 XK_KP_2     # Slow forward
+ S2 XK_KP_3     # Play forward
+ S3 XK_KP_Enter # Fast forward
+ JL XK_KP_4     # Frame reverse
+ JR XK_KP_1     # Frame forward
+
diff --git a/cinelerra-5.1/thirdparty/src/mjpegtools-2.1.0.patch3 b/cinelerra-5.1/thirdparty/src/mjpegtools-2.1.0.patch3
new file mode 100644 (file)
index 0000000..93a2640
--- /dev/null
@@ -0,0 +1,14 @@
+--- mjpegtools-2.1.0/utils/videodev_mjpeg.h.orig       2019-01-12 11:13:19.901685543 -0700
++++ mjpegtools-2.1.0/utils/videodev_mjpeg.h    2019-01-12 11:14:33.325683843 -0700
+@@ -109,6 +109,11 @@
+ /*
+ Private IOCTL to set up for displaying MJPEG
+ */
++/* bad spelling, see /usr/include/videodev2.h */
++#ifndef BASE_VIDIOCPRIVATE
++#define BASE_VIDIOCPRIVATE BASE_VIDIOC_PRIVATE
++#endif
++
+ #define MJPIOC_G_PARAMS       _IOR ('v', BASE_VIDIOCPRIVATE+0,  struct mjpeg_params)
+ #define MJPIOC_S_PARAMS       _IOWR('v', BASE_VIDIOCPRIVATE+1,  struct mjpeg_params)
+ #define MJPIOC_REQBUFS        _IOWR('v', BASE_VIDIOCPRIVATE+2,  struct mjpeg_requestbuffers)