no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / shuttle.C
1 #ifdef HAVE_SHUTTLE
2 // Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact)
3 // reworked 2019 for cinelerra-gg by William Morrow
4
5 #include "arraylist.h"
6 #include "cstrdup.h"
7 #include "file.h"
8 #include "guicast.h"
9 #include "keys.h"
10 #include "linklist.h"
11 #include "loadfile.h"
12 #include "mainmenu.h"
13 #include "shuttle.h"
14 #include "thread.h"
15
16 #include "mwindow.h"
17 #include "mwindowgui.h"
18 #include "awindow.h"
19 #include "awindowgui.h"
20 #include "cwindow.h"
21 #include "cwindowgui.h"
22 #include "vwindow.h"
23 #include "vwindowgui.h"
24
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33
34 #include <X11/Xlib.h>
35 #include <X11/keysym.h>
36
37 static Time milliTimeClock()
38 {
39         struct timeval tv;
40         gettimeofday(&tv, 0);
41         return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
42 }
43
44 KeySymMapping KeySymMapping::key_sym_mapping[] = {
45 // button keycodes
46         { "XK_Button_1", XK_Button_1 },
47         { "XK_Button_2", XK_Button_2 },
48         { "XK_Button_3", XK_Button_3 },
49         { "XK_Scroll_Up", XK_Scroll_Up },
50         { "XK_Scroll_Down", XK_Scroll_Down },
51 #include "shuttle_keys.h"
52         { NULL, 0 }
53 };
54
55 int KeySymMapping::get_mask(const char *&str)
56 {
57         int mask = 0;
58         while( *str ) {
59                 if( !strncmp("Shift-",str,6) ) {
60                         mask |= ShiftMask;
61                         str += 6;  continue;
62                 }
63                 if( !strncmp("Ctrl-",str,5) ) {
64                         mask |= ControlMask;
65                         str += 5;  continue;
66                 }
67                 else if( !strncmp("Alt-",str,4) ) {
68                         mask |= Mod1Mask;
69                         str += 4;  continue;
70                 }
71                 break;
72         }
73         return mask;
74 }
75
76 SKeySym KeySymMapping::to_keysym(const char *str)
77 {
78         if( !strncmp("FWD_",str, 4) ) {
79                 float speed = atof(str+4) / SHUTTLE_MAX_SPEED;
80                 if( speed > SHUTTLE_MAX_SPEED ) return 0;
81                 int key_code = (SKEY_MAX+SKEY_MIN)/2. +
82                         (SKEY_MAX-SKEY_MIN)/2. * speed;
83                 if( key_code > SKEY_MAX ) key_code = SKEY_MAX;
84                 return key_code;
85         }
86         if( !strncmp("REV_",str, 4) ) {
87                 float speed = atof(str+4) / SHUTTLE_MAX_SPEED;
88                 if( speed > SHUTTLE_MAX_SPEED ) return 0;
89                 int key_code = (SKEY_MAX+SKEY_MIN)/2. -
90                         (SKEY_MAX-SKEY_MIN)/2. * speed;
91                 if( key_code < SKEY_MIN ) key_code = SKEY_MIN;
92                 return key_code;
93         }
94         int mask = get_mask(str);
95         for( KeySymMapping *ksp = &key_sym_mapping[0]; ksp->str; ++ksp )
96                 if( !strcmp(str, ksp->str) )
97                         return SKeySym(ksp->sym, mask);
98         return 0;
99 }
100
101 const char *KeySymMapping::to_string(SKeySym ks)
102 {
103         for( KeySymMapping *ksp = &key_sym_mapping[0]; ksp->sym.key; ++ksp ) {
104                 if( ksp->sym.key == ks.key ) {
105                         static char string[BCSTRLEN];
106                         char *sp = string, *ep = sp+sizeof(string);
107                         if( ks.msk & Mod1Mask ) sp += snprintf(sp, ep-sp, "Alt-");
108                         if( ks.msk & ControlMask ) sp += snprintf(sp, ep-sp, "Ctrl-");
109                         if( ks.msk & ShiftMask ) sp += snprintf(sp, ep-sp, "Shift-");
110                         snprintf(sp, ep-sp, "%s", ksp->str);
111                         return string;
112                 }
113         }
114         if( ks >= SKEY_MIN && ks <= SKEY_MAX ) {
115                 double speed = SHUTTLE_MAX_SPEED *
116                         (ks-(SKEY_MAX+SKEY_MIN)/2.) / ((SKEY_MAX-SKEY_MIN)/2.);
117                 static char text[BCSTRLEN];
118                 sprintf(text, "%s_%0.3f", speed>=0 ? "FWD" : "REV", fabs(speed));
119                 char *bp = strchr(text,'.');
120                 if( bp ) {
121                         char *cp = bp+strlen(bp);
122                         while( --cp>bp && *cp=='0' ) *cp=0;
123                         if( cp == bp ) *cp = 0;
124                 }
125                 return text;
126         }
127         return 0;
128 }
129
130 TransName::TransName(int cin, const char *nm, const char *re)
131 {
132         this->cin = cin;
133         this->name = cstrdup(nm);
134 }
135 TransName::~TransName()
136 {
137         delete [] name;
138 }
139
140 void Translation::init(int def)
141 {
142         is_default = def;
143         is_key = 0;
144         first_release_stroke = 0;
145         pressed = 0;
146         released = 0;
147         keysym_down = 0;
148 }
149
150 Translation::Translation(Shuttle *shuttle)
151  : modifiers(this)
152 { // initial default translation
153         init(1);
154         this->shuttle = shuttle;
155         this->name = cstrdup("Default");
156         names.append(new TransName(FOCUS_DEFAULT, name, ""));
157         key_down[K6].add_stroke(XK_Button_1, 1);
158         key_up[K6].add_stroke(XK_Button_1, 0);
159         key_down[K7].add_stroke(XK_Button_2, 1);
160         key_up[K7].add_stroke(XK_Button_2, 0);
161         key_down[K8].add_stroke(XK_Button_3, 1);
162         key_up[K8].add_stroke(XK_Button_3, 0);
163         jog[JL].add_stroke(XK_Scroll_Up, 1);
164         jog[JL].add_stroke(XK_Scroll_Up, 0);
165         jog[JR].add_stroke(XK_Scroll_Down, 0);
166         jog[JR].add_stroke(XK_Scroll_Down, 1);
167 }
168
169 Translation::Translation(Shuttle *shuttle, const char *name)
170  : modifiers(this)
171 {
172         init(0);
173         this->shuttle = shuttle;
174         this->name = cstrdup(name);
175 }
176
177 Translation::~Translation()
178 {
179         delete [] name;
180 }
181
182 void Translation::clear()
183 {
184         names.remove_all_objects();
185         init(0);
186         for( int i=0; i<NUM_KEYS; ++i ) key_down[i].clear();
187         for( int i=0; i<NUM_KEYS; ++i ) key_up[i].clear();
188         for( int i=0; i<NUM_SHUTTLES; ++i ) shuttles[i].clear();
189         for( int i=0; i<NUM_JOGS; ++i ) jog[i].clear();
190 }
191
192 void Translation::append_stroke(SKeySym sym, int press)
193 {
194         Stroke *s = pressed_strokes->append();
195         s->keysym = sym;
196         s->press = press;
197 }
198
199 void Translation::add_keysym(SKeySym sym, int press_release)
200 {
201 //printf("add_keysym(0x%x, %d)\n", (int)sym, press_release);
202         switch( press_release ) {
203         case PRESS:
204                 append_stroke(sym, 1);
205                 modifiers.mark_as_down(sym, 0);
206                 break;
207         case RELEASE:
208                 append_stroke(sym, 0);
209                 modifiers.mark_as_up(sym);
210                 break;
211         case HOLD:
212                 append_stroke(sym, 1);
213                 modifiers.mark_as_down(sym, 1);
214                 break;
215         case PRESS_RELEASE:
216         default:
217                 if( first_release_stroke ) {
218                         modifiers.re_press();
219                         first_release_stroke = 0;
220                 }
221                 if( keysym_down ) {
222                         append_stroke(keysym_down, 0);
223                 }
224                 append_stroke(sym, 1);
225                 keysym_down = sym;
226                 break;
227         }
228 }
229
230 void Translation::add_release(int all_keys)
231 {
232 //printf("add_release(%d)\n", all_keys);
233         modifiers.release(all_keys);
234         if( !all_keys )
235                 pressed_strokes = released_strokes;
236         if( keysym_down ) {
237                 append_stroke(keysym_down, 0);
238                 keysym_down = 0;
239         }
240         first_release_stroke = 1;
241 }
242
243 void Translation::add_keystroke(const char *keySymName, int press_release)
244 {
245         SKeySym sym;
246
247         if( is_key && !strncmp(keySymName, "RELEASE", 8) ) {
248                 add_release(0);
249                 return;
250         }
251         sym = KeySymMapping::to_keysym(keySymName);
252         if( sym != 0 ) {
253                 add_keysym(sym, press_release);
254         }
255         else
256                 fprintf(stderr, "unrecognized KeySym: %s\n", keySymName);
257 }
258
259 void Translation::add_string(char *&str)
260 {
261         int delim = *str++;
262         if( !delim ) return;
263         while( str && *str && *str!=delim ) {
264                 if( *str < ' ' || *str > '~' ) continue;
265                 int mask = KeySymMapping::get_mask((const char *&)str);
266                 if( str[0] == '\\' && str[1] ) ++str;
267                 add_keysym(SKeySym(*str++, mask), PRESS_RELEASE);
268                 mask = 0;
269         }
270         if( *str == delim ) ++str;
271 }
272
273 int Translation::start_line(const char *key)
274 {
275         pressed_strokes = 0;
276         released_strokes = 0;
277         pressed = released = 0;
278         is_key = 0;
279         if( !strcasecmp("JL", key) ) {
280                 pressed = &jog[0];
281         }
282         else if( !strcasecmp("JR", key) ) {
283                 pressed = &jog[1];
284         }
285         else {
286                 char c = 0;  int k = -1, n = 0;
287                 if( sscanf(key, "%c%d%n", &c, &k, &n) != 2 ) return 1;
288                 switch( c ) {
289                 case 'K': case 'k':
290                         --k;
291                         if( k >= K1 && k <= K15 ) {
292                                 pressed = &key_down[k];
293                                 released = &key_up[k];
294                                 is_key = 1;
295                         }
296                         break;
297                 case 'S': case 's':
298                         if( k >= S_7 && k <= S7 ) {
299                                 pressed = &shuttles[k-S_7];
300                         }
301                         break;
302                 }
303                 if( !pressed ) {
304                         fprintf(stderr, "bad key name: [%s]%s\n", name, key);
305                         return 1;
306                 }
307                 if( pressed->first ) {
308                         fprintf(stderr, "dupl key name: [%s]%s\n", name, key);
309                         return 1;
310                 }
311         }
312         pressed_strokes = pressed;
313         released_strokes = released;
314         return 0;
315 }
316
317 void Translation::print_stroke(Stroke *s)
318 {
319         if( !s ) return;
320         const char *cp = KeySymMapping::to_string(s->keysym);
321         if( !cp ) { printf("0x%x", (int)s->keysym); cp = "???"; }
322         printf("%s/%c ", cp, s->press ? 'D' : 'U');
323 }
324
325 void Translation::print_strokes(const char *name, const char *up_dn, Strokes *strokes)
326 {
327         printf("%s[%s]: ", name, up_dn);
328         for( Stroke *s=strokes->first; s; s=s->next )
329                 print_stroke(s);
330         printf("\n");
331 }
332
333 void Translation::finish_line()
334 {
335 //printf("finish_line()\n");
336         if( is_key ) {
337                 add_release(0);
338         }
339         add_release(1);
340 }
341
342 void Translation::print_line(const char *key)
343 {
344         if( is_key ) {
345                 print_strokes(key, "D", pressed);
346                 print_strokes(key, "U", released);
347         }
348         else {
349                 print_strokes(key, "", pressed);
350         }
351         printf("\n");
352 }
353
354 // press values in Modifiers:
355 // PRESS -> down
356 // HOLD -> held
357 // PRESS_RELEASE -> released, but to be re-pressed if necessary
358 // RELEASE -> up
359
360 void Modifiers::mark_as_down(SKeySym sym, int hold)
361 {
362         Modifiers &modifiers = *this;
363         for( int i=0; i<size(); ++i ) {
364                 Stroke &s = modifiers[i];
365                 if( s.keysym == sym ) {
366                         s.press = hold ? HOLD : PRESS;
367                         return;
368                 }
369         }
370         Stroke &s = append();
371         s.keysym = sym;
372         s.press = hold ? HOLD : PRESS;
373 }
374
375 void Modifiers::mark_as_up(SKeySym sym)
376 {
377         Modifiers &modifiers = *this;
378         for( int i=0; i<size(); ++i ) {
379                 Stroke &s = modifiers[i];
380                 if( s.keysym == sym ) {
381                         s.press = RELEASE;
382                         return;
383                 }
384         }
385 }
386
387 void Modifiers::release(int allkeys)
388 {
389         Modifiers &modifiers = *this;
390         for( int i=0; i<size(); ++i ) {
391                 Stroke &s = modifiers[i];
392                 if( s.press == PRESS ) {
393                         trans->append_stroke(s.keysym, 0);
394                         s.press = PRESS_RELEASE;
395                 }
396                 else if( allkeys && s.press == HOLD ) {
397                         trans->append_stroke(s.keysym, 0);
398                         s.press = RELEASE;
399                 }
400         }
401 }
402
403 void Modifiers::re_press()
404 {
405         Modifiers &modifiers = *this;
406         for( int i=0; i<size(); ++i ) {
407                 Stroke &s = modifiers[i];
408                 if( s.press == PRESS_RELEASE ) {
409                         trans->append_stroke(s.keysym, 1);
410                         s.press = PRESS;
411                 }
412         }
413 }
414
415
416 Shuttle::Shuttle(MWindow *mwindow)
417  : Thread(1, 0, 0)
418 {
419         this->mwindow = mwindow;
420
421         fd = -1;
422         wdw = 0;
423         win = 0;
424         msk = 0;
425         rx = ry = 0;
426         wx = wy = 0;
427         jogvalue = 0xffff;
428         shuttlevalue = 0xffff;
429         dev_index = -1;
430
431         done = -1;
432         failed = 0;
433         first_time = 1;
434         tr = 0;
435         debug = 0;
436         usb_direct = 0;
437
438         last_translation = 0;
439         last_focused = 0;
440
441 #ifdef HAVE_SHUTTLE_USB
442         devsh = 0;
443         claimed = -1;
444         last_jog = 0;
445         last_shuttle = 0;
446         last_btns = 0;
447 #endif
448
449         default_translation = new Translation(this);
450         config_path = 0;
451         config_mtime = 0;
452         ev.type = ~0;
453 }
454
455 Shuttle::~Shuttle()
456 {
457         stop();
458         delete default_translation;
459         delete [] config_path;
460 }
461
462 int Shuttle::send_button(unsigned int button, int press)
463 {
464         if( debug )
465                 printf("btn: %u %d\n", button, press);
466         XButtonEvent *b = new XButtonEvent();
467         memset(b, 0, sizeof(*b));
468         b->type = press ? ButtonPress : ButtonRelease;
469         b->time = milliTimeClock();
470         b->send_event = 1;
471         b->display = wdw->top_level->display;
472         b->root = wdw->top_level->rootwin;
473         b->window = win;
474         b->x_root = rx;
475         b->y_root = ry;
476         b->x = wx;
477         b->y = wy;
478         b->state = msk;
479         b->button = button;
480         b->same_screen = 1;
481         wdw->top_level->put_event((XEvent *) b);
482         return 0;
483 }
484 int Shuttle::send_keycode(unsigned key, unsigned msk, int press, int send)
485 {
486         if( debug ) {
487                 const char *cp = !send ? 0 :
488                         KeySymMapping::to_string(SKeySym(key, msk));
489                 if( cp )
490                         printf("key: %s %d\n", cp, press);
491                 else
492                         printf("key: %04x/%04x %d\n", key, msk, press);
493         }
494         XKeyEvent *k = new XKeyEvent();
495         memset(k, 0, sizeof(*k));
496         k->type = press ? KeyPress : KeyRelease;
497         k->time = milliTimeClock();
498         k->send_event = send;
499         k->display = wdw->top_level->display;
500         k->root = wdw->top_level->rootwin;
501         k->window = win;
502         k->x_root = rx;
503         k->y_root = ry;
504         k->x = wx;
505         k->y = wy;
506         k->state = msk;
507 // also clear modifiers state if a key is to be released
508         if( !press ) k->state = 0;
509         k->keycode = key;
510         k->same_screen = 1;
511         wdw->top_level->put_event((XEvent *) k);
512         return 0;
513 }
514
515 int Shuttle::send_keysym(SKeySym keysym, int press)
516 {
517         return keysym >= XK_Button_1 && keysym <= XK_Scroll_Down ?
518                 send_button((unsigned int)keysym - XK_Button_0, press) :
519                 send_keycode(keysym.key, keysym.msk, press, 1);
520 //      unsigned int keycode = XKeysymToKeycode(wdw->top_level->display, keysym);
521 //      return send_keycode(keycode, press, 0);
522 }
523
524
525 static Stroke *fetch_stroke(Translation *translation, int kjs, int index)
526 {
527         Stroke *ret = 0;
528         if( translation ) {
529                 switch( kjs ) {
530                 default:
531                 case KJS_KEY_DOWN:  ret = translation->key_down[index].first;     break;
532                 case KJS_KEY_UP:    ret = translation->key_up[index].first;       break;
533                 case KJS_JOG:       ret = translation->jog[index].first;          break;
534                 case KJS_SHUTTLE:   ret = translation->shuttles[index-S_7].first; break;
535                 }
536         }
537         return ret;
538 }
539
540 void Shuttle::send_stroke_sequence(int kjs, int index)
541 {
542         if( !wdw ) return;
543         Stroke *s = fetch_stroke(tr, kjs, index);
544         if( !s ) s = fetch_stroke(default_translation, kjs, index);
545         while( s ) {
546                 send_keysym(s->keysym, s->press);
547                 s = s->next;
548         }
549 }
550
551 void Shuttle::key(unsigned short code, unsigned int value)
552 {
553         code -= EVENT_CODE_KEY1;
554         if( code >= NUM_KEYS ) {
555                 fprintf(stderr, "key(%d, %d) out of range\n", code + EVENT_CODE_KEY1, value);
556                 return;
557         }
558 // Show help if Alt is pressed on the keyboard together with a shuttle button
559         if( wdw && wdw->alt_down() && value ) {
560                 wdw->context_help_show("Shuttle key default arrangement");
561                 return;
562         }
563         send_stroke_sequence(value ? KJS_KEY_DOWN : KJS_KEY_UP, code);
564 }
565
566
567 void Shuttle::shuttle(int value)
568 {
569         if( value < S_7 || value > S7 ) {
570                 fprintf(stderr, "shuttle(%d) out of range\n", value);
571                 return;
572         }
573         if( value != (int)shuttlevalue ) {
574                 shuttlevalue = value;
575                 send_stroke_sequence(KJS_SHUTTLE, value);
576         }
577 }
578
579 // Due to a bug (?) in the way Linux HID handles the ShuttlePro, the
580 // center position is not reported for the shuttle wheel.       Instead,
581 // a jog event is generated immediately when it returns.        We check to
582 // see if the time since the last shuttle was more than a few ms ago
583 // and generate a shuttle of 0 if so.
584 //
585 // Note, this fails if jogvalue happens to be 0, as we don't see that
586 // event either!
587 void Shuttle::jog(unsigned int value)
588 {
589         if( jogvalue != 0xffff ) {
590                 value = value & 0xff;
591                 int direction = ((value - jogvalue) & 0x80) ? -1 : 1;
592                 int index = direction > 0 ? 1 : 0;
593                 while( jogvalue != value ) {
594                         // driver fails to send an event when jogvalue == 0
595                         if( jogvalue != 0 ) {
596                                 send_stroke_sequence(KJS_JOG, index);
597                         }
598                         jogvalue = (jogvalue + direction) & 0xff;
599                 }
600         }
601         jogvalue = value;
602 }
603
604 void Shuttle::jogshuttle(unsigned short code, unsigned int value)
605 {
606         switch( code ) {
607         case EVENT_CODE_JOG:
608                 jog(value);
609                 break;
610         case EVENT_CODE_SHUTTLE:
611                 shuttle(value);
612                 break;
613         case EVENT_CODE_HI_JOG:  // redundant report of JOG value*120
614                 break;
615         default:
616                 fprintf(stderr, "jogshuttle(%d, %d) invalid code\n", code, value);
617                 break;
618         }
619 }
620
621 static const struct shuttle_dev {
622         const char *path;
623         unsigned vendor, product;
624 } shuttle_devs[] = {
625         { "/dev/input/by-id/usb-Contour_Design_ShuttleXpress-event-if00",
626                 0x0b33, 0x0020 },
627         { "/dev/input/by-id/usb-Contour_Design_ShuttleXpress-event-mouse",
628                 0x0b33, 0x0020 },
629         { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-if00",
630                 0x0b33, 0x0030 },
631         { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-mouse",
632                 0x0b33, 0x0030 },
633         { "/dev/input/by-id/usb-Contour_Design_ShuttlePro-event-if00",
634                 0x0b33, 0x0030 },
635         { "/dev/input/by-id/usb-Contour_Design_ShuttlePro-event-mouse",
636                 0x0b33, 0x0030 },
637         { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-joystick",
638                 0x0b33, 0x0030 },
639 };
640
641 #ifdef HAVE_SHUTTLE_USB
642 void Shuttle::usb_probe(int idx)
643 {
644         int ret = libusb_init(0);
645         if( ret < 0 ) return;
646         claimed = 0;
647         const struct shuttle_dev *s = &shuttle_devs[idx];
648         devsh = libusb_open_device_with_vid_pid(0, s->vendor, s->product);
649         if( devsh ) {
650                 int sh_iface = SHUTTLE_INTERFACE;
651                 libusb_detach_kernel_driver(devsh, sh_iface);
652                 ret = libusb_claim_interface(devsh, sh_iface);
653                 if( ret >= 0 ) claimed = 1;
654         }
655         if( !claimed )
656                 usb_done();
657 }
658
659 void Shuttle::usb_done()
660 {
661         if( devsh ) {
662                 if( claimed > 0 ) {
663                         int sh_iface = SHUTTLE_INTERFACE;
664                         libusb_release_interface(devsh, sh_iface);
665                         libusb_attach_kernel_driver(devsh, sh_iface);
666                         claimed = 0;
667                 }
668                 libusb_close(devsh);
669                 devsh = 0;
670         }
671         if( claimed >= 0 ) {
672                 libusb_exit(0);
673                 claimed = -1;
674         }
675 }
676 #endif
677
678 int Shuttle::probe()
679 {
680         struct stat st;
681         int ret = sizeof(shuttle_devs) / sizeof(shuttle_devs[0]);
682         while( --ret >= 0 && stat(shuttle_devs[ret].path , &st) );
683         return ret;
684 }
685
686 void Shuttle::start(int idx)
687 {
688         this->dev_index = idx;
689         first_time = 1;
690         done = 0;
691         Thread::start();
692 }
693
694 void Shuttle::stop()
695 {
696         if( running() && !done ) {
697                 done = 1;
698                 cancel();
699                 join();
700         }
701 }
702
703 BC_WindowBase *Shuttle::owns(BC_WindowBase *wdw, Window win)
704 {
705         if( wdw->win == win ) return wdw;
706         if( (wdw=wdw->top_level)->win == win ) return wdw;
707         for( int i=wdw->popups.size(); --i>=0; )
708                 if( wdw->popups[i]->win == win ) return wdw;
709         return 0;
710 }
711
712 int Shuttle::get_focused_window_translation()
713 {
714         MWindowGUI *gui = mwindow->gui;
715         Display *dpy = gui->display;
716         Window focus = 0;
717         int ret = 0, revert = 0;
718         char win_title[BCTEXTLEN];  win_title[0] = 0;
719         gui->lock_window("Shuttle::get_focused_window_translation");
720         XGetInputFocus(dpy, &focus, &revert);
721         if( last_focused != focus ) {
722                 last_focused = focus;
723                 Atom prop = XInternAtom(dpy, "WM_NAME", False);
724                 Atom type;  int form;
725                 unsigned long remain, len;
726                 unsigned char *list;
727                 if( XGetWindowProperty(dpy, focus, prop, 0, sizeof(win_title)-1, False,
728                         AnyPropertyType, &type, &form, &len, &remain, &list) == Success ) {
729                         len = len*(form/8) - remain;
730                         memcpy(win_title, list, len);
731                         win_title[len] = 0;
732                         XFree(list);
733                         if( debug )
734                                 printf("new focus: %08x %s\n", (unsigned)focus, win_title);
735                 }
736                 else {
737                         last_focused = 0;
738                         fprintf(stderr, "XGetWindowProperty failed for window 0x%x\n",
739                                         (int)focus);
740                         ret = 1;
741                 }
742         }
743         gui->unlock_window();
744         if( ret ) return -1;
745
746         this->wdw = 0;  this->win = 0;
747         this->wx = 0;   this->wy = 0;
748         this->rx = 0;   this->ry = 0;
749         this->msk = 0;
750         BC_WindowBase *wdw = 0;
751         int cin = -1;
752         if( (wdw=owns(mwindow->gui, focus)) != 0 )
753                 cin = FOCUS_MWINDOW;
754         else if( (wdw=owns(mwindow->awindow->gui, focus)) != 0 )
755                 cin = FOCUS_AWINDOW;
756         else if( (wdw=owns(mwindow->cwindow->gui, focus)) != 0 ) {
757                 if( mwindow->cwindow->gui->canvas->get_fullscreen() )
758                         wdw = mwindow->cwindow->gui->canvas->get_canvas();
759                 cin = FOCUS_CWINDOW;
760         }
761         else if( mwindow->gui->mainmenu->load_file->thread->running() &&
762                  (wdw=mwindow->gui->mainmenu->load_file->thread->window) != 0 &&
763                  wdw->win == focus )
764                 cin = FOCUS_LOAD;
765         else {
766                 int i = mwindow->vwindows.size();
767                 while( --i >= 0 ) {
768                         VWindow *vwdw =  mwindow->vwindows[i];
769                         if( !vwdw->is_running() ) continue;
770                         if( (wdw=owns(vwdw->gui, focus)) != 0 ) {
771                                 if( vwdw->gui->canvas->get_fullscreen() )
772                                         wdw = vwdw->gui->canvas->get_canvas();
773                                 cin = FOCUS_VIEWER;  break;
774                         }
775                 }
776         }
777         if( cin < 0 ) return -1;
778         Window root = 0, child = 0;
779         int root_x = 0, root_y = 0, win_x = 0, win_y = 0, x = 0, y = 0;
780         unsigned int mask = 0, width = 0, height = 0, border_width = 0, depth = 0;
781         wdw->lock_window("Shuttle::get_focused_window_translation 1");
782         if( XQueryPointer(wdw->top_level->display, focus, &root, &child,
783                         &root_x, &root_y, &win_x, &win_y, &mask) ) {
784                 if( !child ) {
785                         if( wdw->active_menubar )
786                                 child = wdw->active_menubar->win;
787                         else if( wdw->active_popup_menu )
788                                 child = wdw->active_popup_menu->win;
789                         else if( wdw->active_subwindow )
790                                 child = wdw->active_subwindow->win;
791                         else
792                                 child = wdw->win;
793                 }
794                 else
795                         XGetGeometry(wdw->top_level->display, child, &root, &x, &y,
796                                 &width, &height, &border_width, &depth);
797         }
798         wdw->unlock_window();
799         if( !child || !wdw->match_window(child) ) return -1;
800 // success
801         this->wdw = wdw;
802         this->win = child;
803         this->msk = mask;
804         this->rx = root_x;
805         this->ry = root_y;
806         this->wx = win_x - x;
807         this->wy = win_y - y;
808         for( tr=translations.first; tr; tr=tr->next ) {
809                 if( tr->is_default ) return 1;
810                 for( int i=0; i<tr->names.size(); ++i ) {
811                         TransName *name = tr->names[i];
812                         if( name->cin == cin ) return 1;
813                 }
814         }
815         tr = default_translation;
816         return 0;
817 }
818
819 int Shuttle::load_translation()
820 {
821         if( read_config_file() > 0 )
822                 return done = 1;
823         if( get_focused_window_translation() < 0 )
824                 return 0;
825         if( last_translation != tr ) {
826                 last_translation = tr;
827                 if( debug )
828                         printf("new translation: %s\n", tr->name);
829         }
830         return 0;
831 }
832
833 void Shuttle::handle_event()
834 {
835         if( load_translation() ) return;
836 //      if( debug )
837 //              printf("event: (%d, %d, 0x%x)\n", ev.type, ev.code, ev.value);
838         switch( ev.type ) {
839         case EVENT_TYPE_DONE:
840         case EVENT_TYPE_ACTIVE_KEY:
841                 break;
842         case EVENT_TYPE_KEY:
843                 key(ev.code, ev.value);
844                 break;
845         case EVENT_TYPE_JOGSHUTTLE:
846                 jogshuttle(ev.code, ev.value);
847                 break;
848         default:
849                 fprintf(stderr, "handle_event() invalid type code\n");
850                 break;
851         }
852 }
853
854 void Shuttle::run()
855 {
856         if( dev_index < 0 ) return;
857         const char *dev_name = shuttle_devs[dev_index].path;
858
859 #ifdef HAVE_SHUTTLE_USB
860         if( usb_direct )
861                 usb_probe(dev_index);
862
863         disable_cancel();
864         while( devsh && !done ) {
865                 int len = 0;
866                 static const int IN_ENDPOINT = 0x81;
867                 unsigned char dat[5];
868                 int ret = libusb_interrupt_transfer(devsh,
869                                 IN_ENDPOINT, dat, sizeof(dat), &len, 100);
870                 if( ret != 0 ) {
871                         if( ret == LIBUSB_ERROR_TIMEOUT ) continue;
872                         printf("shuttle: %s\n  %s\n",
873                                 dev_name, libusb_strerror((libusb_error)ret));
874                         sleep(1);  continue;
875                 }
876                 if( load_translation() ) break;
877                 if( debug ) {
878                         printf("shuttle: ");
879                         for( int i=0; i<len; ++i ) printf(" %02x", dat[i]);
880                         printf("\n");
881                 }
882                 if( last_shuttle != dat[0] )
883                         shuttle((char)(last_shuttle = dat[0]));
884
885                 if( last_jog != dat[1] )
886                         jog(last_jog = dat[1]);
887
888                 unsigned btns = (dat[4]<<8) | dat[3];
889                 unsigned dif = last_btns ^ btns;
890                 if( dif ) {
891                         last_btns = btns;
892                         for( int i=0; i<15; ++i ) {
893                                 unsigned msk = 1 << i;
894                                 if( !(dif & msk) ) continue;
895                                 key(i+EVENT_CODE_KEY1, btns & msk ? 1 : 0);
896                         }
897                 }
898         }
899         usb_done();
900 #endif
901         usb_direct = 0;
902         enable_cancel();
903
904         for( ; !done; sleep(1) ) {
905                 fd = open(dev_name, O_RDONLY);
906                 if( fd < 0 ) {
907                         perror(dev_name);
908                         if( first_time ) break;
909                         continue;
910                 }
911                 if( 1 || !ioctl(fd, EVIOCGRAB, 1) ) { // exclusive access
912                         first_time = 0;
913                         while( !done ) {
914                                 int ret = read(fd, &ev, sizeof(ev));
915                                 if( done ) break;
916                                 if( ret != sizeof(ev) ) {
917                                         if( ret < 0 ) { perror("read event"); break; }
918                                         fprintf(stderr, "bad read: %d\n", ret);
919                                         break;
920                                 }
921                                 handle_event();
922                         }
923                 }
924                 else
925                         perror( "evgrab ioctl" );
926                 close(fd);
927         }
928         done = 2;
929 }
930
931 int Shuttle::read_config_file()
932 {
933         if( !config_path ) {
934                 const char *env;
935                 config_path = (env=getenv("SHUTTLE_CONFIG_FILE")) != 0 ? cstrdup(env) :
936                         (env=getenv("HOME")) != 0 ? cstrcat(2, env, "/.shuttlerc") : 0;
937                 if( !config_path ) { fprintf(stderr, "no config file\n");  return 1; }
938         }
939         struct stat st;
940         if( stat(config_path, &st) ) {
941                 perror(config_path);
942                 char shuttlerc[BCTEXTLEN];
943                 snprintf(shuttlerc, sizeof(shuttlerc), "%s/shuttlerc",
944                                 File::get_cindat_path());
945                 if( stat(shuttlerc, &st) ) {
946                         perror(shuttlerc);
947                         return 1;
948                 }
949                 delete [] config_path;
950                 config_path = cstrdup(shuttlerc);
951         }
952         if( config_mtime > 0 &&
953             config_mtime == st.st_mtime ) return 0;
954         FILE *fp = fopen(config_path, "r");
955         if( !fp ) {
956                 perror(config_path);
957                 return 1;
958         }
959
960         config_mtime = st.st_mtime;
961         translations.clear();
962         debug = 0;
963         usb_direct = 0;
964 #define ws(ch) (ch==' ' || ch=='\t')
965         char line[BCTEXTLEN], *cp;
966         Translation *trans = 0;
967         int no = 0;
968         int ret = fgets(cp=line,sizeof(line),fp) ? 0 : -1;
969         if( !ret ) ++no;
970 // lines
971         while( !ret ) {
972 // translation names
973                 while( !ret && *cp == '[' ) {
974                         char *name = ++cp;
975                         while( *cp && *cp != ']' ) ++cp;
976                         *cp++ = 0;
977                         if( !name || !*name ) { ret = 1;  break; }
978                         int cin = -1;
979                         if( !strcasecmp("default", name) )
980                                 cin = FOCUS_DEFAULT;
981                         else if( !strcasecmp("cinelerra", name) )
982                                 cin = FOCUS_MWINDOW;
983                         else if( !strcasecmp("resources", name) )
984                                 cin = FOCUS_AWINDOW;
985                         else if( !strcasecmp("composer", name) )
986                                 cin = FOCUS_CWINDOW;
987                         else if( !strcasecmp("viewer", name) )
988                                 cin = FOCUS_VIEWER;
989                         else if( !strcasecmp("load", name) )
990                                 cin = FOCUS_LOAD;
991                         else {
992                                 fprintf(stderr, "unknown focus target window: %s\n",
993                                          name);
994                                 ret = 1;  break;
995                         }
996                         if( !trans ) {
997                                 if( cin == FOCUS_DEFAULT ) {
998                                         trans = default_translation;
999                                         trans->clear();
1000                                 }
1001                                 else {
1002                                         trans = new Translation(this, name);
1003                                 }
1004                         }
1005                         while( ws(*cp) ) ++cp;
1006                         trans->names.append(new TransName(cin, name, cp));
1007                         ret = fgets(cp=line,sizeof(line),fp) ? 0 : 1;
1008                         if( ret ) {
1009                                 fprintf(stderr, "hit eof, no translation def for: %s\n",
1010                                         trans->names.last()->name);
1011                                 ret = 1;  break;
1012                         }
1013                         ++no;
1014                 }
1015                 if( ret ) break;
1016                 if( debug && trans ) {
1017                         printf("------------------------\n");
1018                         TransNames &names = trans->names;
1019                         for( int i=0; i<names.size(); ++i ) {
1020                                 TransName *tp = names[i];
1021                                 printf("[%s] # %d\n\n", tp->name, tp->cin);
1022                         }
1023                 }
1024 // rules lines: "tok <stroke list>\n"
1025                 while( !ret && *cp != '[' ) {
1026                         const char *key = 0, *tok = 0;
1027                         while( ws(*cp) ) ++cp;
1028                         if( !*cp || *cp == '\n' || *cp == '#' ) goto skip;
1029                         tok = cp;
1030                         while( *cp && !ws(*cp) && *cp != '\n' ) ++cp;
1031                         *cp++ = 0;
1032                         if( !strcmp(tok, "DEBUG") ) {
1033                                 debug = 1;  goto skip;
1034                         }
1035                         if( !strcmp(tok, "USB_DIRECT") ) {
1036                                 usb_direct = 1;  goto skip;
1037                         }
1038                         key = tok;
1039                         if( !trans ) {
1040                                 fprintf(stderr, "no translation section defining key: %s\n", key);
1041                                 ret = 1;  break;
1042                         }
1043         
1044                         ret = trans->start_line(key);
1045                         while( !ret && *cp && *cp != '\n' ) {
1046                                 while( ws(*cp) ) ++cp;
1047                                 if( !*cp || *cp == '#' || *cp == '\n' ) break;
1048                                 if( *cp == '"' ) {
1049                                         while( *cp ) {
1050                                                 if( *cp == '"' )
1051                                                         trans->add_string(cp);
1052                                                 while( ws(*cp) ) ++cp;
1053                                                 if( !*cp || *cp == '#' || *cp == '\n' ) break;
1054                                                 tok = cp;
1055                                                 while( *cp && !ws(*cp) && *cp != '\n' ) ++cp;
1056                                                 *cp = 0;
1057                                                 SKeySym sym = KeySymMapping::to_keysym(tok);
1058                                                 if( !sym ) {
1059                                                         fprintf(stderr, "unknown keysym: %s\n", tok);
1060                                                         ret = 1;  break;
1061                                                 }
1062                                                 trans->add_keysym(sym, PRESS_RELEASE);
1063                                         }
1064                                         continue;
1065                                 }
1066                                 tok = cp;
1067                                 while( *cp && !ws(*cp) && *cp!='/' && *cp != '\n' ) ++cp;
1068                                 int dhu = PRESS_RELEASE;
1069                                 if( *cp == '/' ) {
1070                                         *cp++ = 0;
1071                                         switch( *cp ) {
1072                                         case 'D':  dhu = PRESS;    break;
1073                                         case 'H':  dhu = HOLD;     break;
1074                                         case 'U':  dhu = RELEASE;  break;
1075                                         default:
1076                                                 fprintf(stderr, "invalid up/down modifier [%s]%s: '%c'\n",
1077                                                         trans->name, tok, *cp);
1078                                                 ret = 1;  break;
1079                                         }
1080                                         ++cp;
1081                                 }
1082                                 else
1083                                         *cp++ = 0;
1084                                 trans->add_keystroke(tok, dhu);
1085                         }
1086                         if( ret ) break;
1087                         trans->finish_line();
1088                         if( debug )
1089                                 trans->print_line(key);
1090                 skip:   ret = fgets(cp=line,sizeof(line),fp) ? 0 : -1;
1091                         if( !ret ) ++no;
1092                 }
1093                 if( trans ) {
1094                         if( trans != default_translation )
1095                                 translations.append(trans);
1096                         trans = 0;
1097                 }
1098         }
1099         if( ret > 0 )
1100                 fprintf(stderr, "shuttle config err file: %s, line:%d\n",
1101                         config_path, no);
1102
1103         fclose(fp);
1104         return ret;
1105 }
1106 #endif