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