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