2 // Copyright 2013 Eric Messick (FixedImagePhoto.com/Contact)
3 // reworked 2019 for cinelerra-gg by William Morrow
17 #include "mwindowgui.h"
19 #include "awindowgui.h"
21 #include "cwindowgui.h"
23 #include "vwindowgui.h"
35 #include <X11/keysym.h>
37 static Time milliTimeClock()
41 return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
44 KeySymMapping KeySymMapping::key_sym_mapping[] = {
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"
55 int KeySymMapping::get_mask(const char *&str)
59 if( !strncmp("Shift-",str,6) ) {
63 if( !strncmp("Ctrl-",str,5) ) {
67 else if( !strncmp("Alt-",str,4) ) {
76 SKeySym KeySymMapping::to_keysym(const char *str)
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;
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;
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);
101 const char *KeySymMapping::to_string(SKeySym ks)
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);
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,'.');
121 char *cp = bp+strlen(bp);
122 while( --cp>bp && *cp=='0' ) *cp=0;
123 if( cp == bp ) *cp = 0;
130 TransName::TransName(int cin, const char *nm, const char *re)
133 this->name = cstrdup(nm);
135 TransName::~TransName()
140 void Translation::init(int def)
144 first_release_stroke = 0;
150 Translation::Translation(Shuttle *shuttle)
152 { // initial default translation
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);
169 Translation::Translation(Shuttle *shuttle, const char *name)
173 this->shuttle = shuttle;
174 this->name = cstrdup(name);
177 Translation::~Translation()
182 void Translation::clear()
184 names.remove_all_objects();
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();
192 void Translation::append_stroke(SKeySym sym, int press)
194 Stroke *s = pressed_strokes->append();
199 void Translation::add_keysym(SKeySym sym, int press_release)
201 //printf("add_keysym(0x%x, %d)\n", (int)sym, press_release);
202 switch( press_release ) {
204 append_stroke(sym, 1);
205 modifiers.mark_as_down(sym, 0);
208 append_stroke(sym, 0);
209 modifiers.mark_as_up(sym);
212 append_stroke(sym, 1);
213 modifiers.mark_as_down(sym, 1);
217 if( first_release_stroke ) {
218 modifiers.re_press();
219 first_release_stroke = 0;
222 append_stroke(keysym_down, 0);
224 append_stroke(sym, 1);
230 void Translation::add_release(int all_keys)
232 //printf("add_release(%d)\n", all_keys);
233 modifiers.release(all_keys);
235 pressed_strokes = released_strokes;
237 append_stroke(keysym_down, 0);
240 first_release_stroke = 1;
243 void Translation::add_keystroke(const char *keySymName, int press_release)
247 if( is_key && !strncmp(keySymName, "RELEASE", 8) ) {
251 sym = KeySymMapping::to_keysym(keySymName);
253 add_keysym(sym, press_release);
256 fprintf(stderr, "unrecognized KeySym: %s\n", keySymName);
259 void Translation::add_string(char *&str)
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);
270 if( *str == delim ) ++str;
273 int Translation::start_line(const char *key)
276 released_strokes = 0;
277 pressed = released = 0;
279 if( !strcasecmp("JL", key) ) {
282 else if( !strcasecmp("JR", key) ) {
286 char c = 0; int k = -1, n = 0;
287 if( sscanf(key, "%c%d%n", &c, &k, &n) != 2 ) return 1;
291 if( k >= K1 && k <= K15 ) {
292 pressed = &key_down[k];
293 released = &key_up[k];
298 if( k >= S_7 && k <= S7 ) {
299 pressed = &shuttles[k-S_7];
304 fprintf(stderr, "bad key name: [%s]%s\n", name, key);
307 if( pressed->first ) {
308 fprintf(stderr, "dupl key name: [%s]%s\n", name, key);
312 pressed_strokes = pressed;
313 released_strokes = released;
317 void Translation::print_stroke(Stroke *s)
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');
325 void Translation::print_strokes(const char *name, const char *up_dn, Strokes *strokes)
327 printf("%s[%s]: ", name, up_dn);
328 for( Stroke *s=strokes->first; s; s=s->next )
333 void Translation::finish_line()
335 //printf("finish_line()\n");
342 void Translation::print_line(const char *key)
345 print_strokes(key, "D", pressed);
346 print_strokes(key, "U", released);
349 print_strokes(key, "", pressed);
354 // press values in Modifiers:
357 // PRESS_RELEASE -> released, but to be re-pressed if necessary
360 void Modifiers::mark_as_down(SKeySym sym, int hold)
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;
370 Stroke &s = append();
372 s.press = hold ? HOLD : PRESS;
375 void Modifiers::mark_as_up(SKeySym sym)
377 Modifiers &modifiers = *this;
378 for( int i=0; i<size(); ++i ) {
379 Stroke &s = modifiers[i];
380 if( s.keysym == sym ) {
387 void Modifiers::release(int allkeys)
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;
396 else if( allkeys && s.press == HOLD ) {
397 trans->append_stroke(s.keysym, 0);
403 void Modifiers::re_press()
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);
416 Shuttle::Shuttle(MWindow *mwindow)
419 this->mwindow = mwindow;
428 shuttlevalue = 0xffff;
438 last_translation = 0;
441 #ifdef HAVE_SHUTTLE_USB
449 default_translation = new Translation(this);
458 delete default_translation;
459 delete [] config_path;
462 int Shuttle::send_button(unsigned int button, int press)
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();
471 b->display = wdw->top_level->display;
472 b->root = wdw->top_level->rootwin;
481 wdw->top_level->put_event((XEvent *) b);
484 int Shuttle::send_keycode(unsigned key, unsigned msk, int press, int send)
487 const char *cp = !send ? 0 :
488 KeySymMapping::to_string(SKeySym(key, msk));
490 printf("key: %s %d\n", cp, press);
492 printf("key: %04x/%04x %d\n", key, msk, press);
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;
507 // also clear modifiers state if a key is to be released
508 if( !press ) k->state = 0;
511 wdw->top_level->put_event((XEvent *) k);
515 int Shuttle::send_keysym(SKeySym keysym, int press)
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);
525 static Stroke *fetch_stroke(Translation *translation, int kjs, int index)
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;
540 void Shuttle::send_stroke_sequence(int kjs, int index)
543 Stroke *s = fetch_stroke(tr, kjs, index);
544 if( !s ) s = fetch_stroke(default_translation, kjs, index);
546 send_keysym(s->keysym, s->press);
551 void Shuttle::key(unsigned short code, unsigned int value)
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);
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");
563 send_stroke_sequence(value ? KJS_KEY_DOWN : KJS_KEY_UP, code);
567 void Shuttle::shuttle(int value)
569 if( value < S_7 || value > S7 ) {
570 fprintf(stderr, "shuttle(%d) out of range\n", value);
573 if( value != (int)shuttlevalue ) {
574 shuttlevalue = value;
575 send_stroke_sequence(KJS_SHUTTLE, value);
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.
585 // Note, this fails if jogvalue happens to be 0, as we don't see that
587 void Shuttle::jog(unsigned int value)
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);
598 jogvalue = (jogvalue + direction) & 0xff;
604 void Shuttle::jogshuttle(unsigned short code, unsigned int value)
610 case EVENT_CODE_SHUTTLE:
613 case EVENT_CODE_HI_JOG: // redundant report of JOG value*120
616 fprintf(stderr, "jogshuttle(%d, %d) invalid code\n", code, value);
621 static const struct shuttle_dev {
623 unsigned vendor, product;
625 { "/dev/input/by-id/usb-Contour_Design_ShuttleXpress-event-if00",
627 { "/dev/input/by-id/usb-Contour_Design_ShuttleXpress-event-mouse",
629 { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-if00",
631 { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-mouse",
633 { "/dev/input/by-id/usb-Contour_Design_ShuttlePro-event-if00",
635 { "/dev/input/by-id/usb-Contour_Design_ShuttlePro-event-mouse",
637 { "/dev/input/by-id/usb-Contour_Design_ShuttlePRO_v2-event-joystick",
641 #ifdef HAVE_SHUTTLE_USB
642 void Shuttle::usb_probe(int idx)
644 int ret = libusb_init(0);
645 if( ret < 0 ) return;
647 const struct shuttle_dev *s = &shuttle_devs[idx];
648 devsh = libusb_open_device_with_vid_pid(0, s->vendor, s->product);
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;
659 void Shuttle::usb_done()
663 int sh_iface = SHUTTLE_INTERFACE;
664 libusb_release_interface(devsh, sh_iface);
665 libusb_attach_kernel_driver(devsh, sh_iface);
681 int ret = sizeof(shuttle_devs) / sizeof(shuttle_devs[0]);
682 while( --ret >= 0 && stat(shuttle_devs[ret].path , &st) );
686 void Shuttle::start(int idx)
688 this->dev_index = idx;
696 if( running() && !done ) {
703 BC_WindowBase *Shuttle::owns(BC_WindowBase *wdw, Window win)
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;
712 int Shuttle::get_focused_window_translation()
714 MWindowGUI *gui = mwindow->gui;
715 Display *dpy = gui->display;
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);
725 unsigned long remain, len;
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);
734 printf("new focus: %08x %s\n", (unsigned)focus, win_title);
738 fprintf(stderr, "XGetWindowProperty failed for window 0x%x\n",
743 gui->unlock_window();
746 this->wdw = 0; this->win = 0;
747 this->wx = 0; this->wy = 0;
748 this->rx = 0; this->ry = 0;
750 BC_WindowBase *wdw = 0;
752 if( (wdw=owns(mwindow->gui, focus)) != 0 )
754 else if( (wdw=owns(mwindow->awindow->gui, focus)) != 0 )
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();
761 else if( mwindow->gui->mainmenu->load_file->thread->running() &&
762 (wdw=mwindow->gui->mainmenu->load_file->thread->window) != 0 &&
766 int i = mwindow->vwindows.size();
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;
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) ) {
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;
795 XGetGeometry(wdw->top_level->display, child, &root, &x, &y,
796 &width, &height, &border_width, &depth);
798 wdw->unlock_window();
799 if( !child || !wdw->match_window(child) ) return -1;
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;
815 tr = default_translation;
819 int Shuttle::load_translation()
821 if( read_config_file() > 0 )
823 if( get_focused_window_translation() < 0 )
825 if( last_translation != tr ) {
826 last_translation = tr;
828 printf("new translation: %s\n", tr->name);
833 void Shuttle::handle_event()
835 if( load_translation() ) return;
837 // printf("event: (%d, %d, 0x%x)\n", ev.type, ev.code, ev.value);
839 case EVENT_TYPE_DONE:
840 case EVENT_TYPE_ACTIVE_KEY:
843 key(ev.code, ev.value);
845 case EVENT_TYPE_JOGSHUTTLE:
846 jogshuttle(ev.code, ev.value);
849 fprintf(stderr, "handle_event() invalid type code\n");
856 if( dev_index < 0 ) return;
857 const char *dev_name = shuttle_devs[dev_index].path;
859 #ifdef HAVE_SHUTTLE_USB
861 usb_probe(dev_index);
864 while( devsh && !done ) {
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);
871 if( ret == LIBUSB_ERROR_TIMEOUT ) continue;
872 printf("shuttle: %s\n %s\n",
873 dev_name, libusb_strerror((libusb_error)ret));
876 if( load_translation() ) break;
879 for( int i=0; i<len; ++i ) printf(" %02x", dat[i]);
882 if( last_shuttle != dat[0] )
883 shuttle((char)(last_shuttle = dat[0]));
885 if( last_jog != dat[1] )
886 jog(last_jog = dat[1]);
888 unsigned btns = (dat[4]<<8) | dat[3];
889 unsigned dif = 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);
904 for( ; !done; sleep(1) ) {
905 fd = open(dev_name, O_RDONLY);
908 if( first_time ) break;
911 if( 1 || !ioctl(fd, EVIOCGRAB, 1) ) { // exclusive access
914 int ret = read(fd, &ev, sizeof(ev));
916 if( ret != sizeof(ev) ) {
917 if( ret < 0 ) { perror("read event"); break; }
918 fprintf(stderr, "bad read: %d\n", ret);
925 perror( "evgrab ioctl" );
931 int Shuttle::read_config_file()
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; }
940 if( stat(config_path, &st) ) {
942 char shuttlerc[BCTEXTLEN];
943 snprintf(shuttlerc, sizeof(shuttlerc), "%s/shuttlerc",
944 File::get_cindat_path());
945 if( stat(shuttlerc, &st) ) {
949 delete [] config_path;
950 config_path = cstrdup(shuttlerc);
952 if( config_mtime > 0 &&
953 config_mtime == st.st_mtime ) return 0;
954 FILE *fp = fopen(config_path, "r");
960 config_mtime = st.st_mtime;
961 translations.clear();
964 #define ws(ch) (ch==' ' || ch=='\t')
965 char line[BCTEXTLEN], *cp;
966 Translation *trans = 0;
968 int ret = fgets(cp=line,sizeof(line),fp) ? 0 : -1;
973 while( !ret && *cp == '[' ) {
975 while( *cp && *cp != ']' ) ++cp;
977 if( !name || !*name ) { ret = 1; break; }
979 if( !strcasecmp("default", name) )
981 else if( !strcasecmp("cinelerra", name) )
983 else if( !strcasecmp("resources", name) )
985 else if( !strcasecmp("composer", name) )
987 else if( !strcasecmp("viewer", name) )
989 else if( !strcasecmp("load", name) )
992 fprintf(stderr, "unknown focus target window: %s\n",
997 if( cin == FOCUS_DEFAULT ) {
998 trans = default_translation;
1002 trans = new Translation(this, name);
1005 while( ws(*cp) ) ++cp;
1006 trans->names.append(new TransName(cin, name, cp));
1007 ret = fgets(cp=line,sizeof(line),fp) ? 0 : 1;
1009 fprintf(stderr, "hit eof, no translation def for: %s\n",
1010 trans->names.last()->name);
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);
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;
1030 while( *cp && !ws(*cp) && *cp != '\n' ) ++cp;
1032 if( !strcmp(tok, "DEBUG") ) {
1033 debug = 1; goto skip;
1035 if( !strcmp(tok, "USB_DIRECT") ) {
1036 usb_direct = 1; goto skip;
1040 fprintf(stderr, "no translation section defining key: %s\n", key);
1044 ret = trans->start_line(key);
1045 while( !ret && *cp && *cp != '\n' ) {
1046 while( ws(*cp) ) ++cp;
1047 if( !*cp || *cp == '#' || *cp == '\n' ) break;
1051 trans->add_string(cp);
1052 while( ws(*cp) ) ++cp;
1053 if( !*cp || *cp == '#' || *cp == '\n' ) break;
1055 while( *cp && !ws(*cp) && *cp != '\n' ) ++cp;
1057 SKeySym sym = KeySymMapping::to_keysym(tok);
1059 fprintf(stderr, "unknown keysym: %s\n", tok);
1062 trans->add_keysym(sym, PRESS_RELEASE);
1067 while( *cp && !ws(*cp) && *cp!='/' && *cp != '\n' ) ++cp;
1068 int dhu = PRESS_RELEASE;
1072 case 'D': dhu = PRESS; break;
1073 case 'H': dhu = HOLD; break;
1074 case 'U': dhu = RELEASE; break;
1076 fprintf(stderr, "invalid up/down modifier [%s]%s: '%c'\n",
1077 trans->name, tok, *cp);
1084 trans->add_keystroke(tok, dhu);
1087 trans->finish_line();
1089 trans->print_line(key);
1090 skip: ret = fgets(cp=line,sizeof(line),fp) ? 0 : -1;
1094 if( trans != default_translation )
1095 translations.append(trans);
1100 fprintf(stderr, "shuttle config err file: %s, line:%d\n",