PrintableKeys = { 0: 0x30, 1: 0x31, 2: 0x32, 3: 0x33, 4: 0x34, 5: 0x35, 6: 0x36, 7: 0x37, 8: 0x38, 9: 0x39, A: 0x61, a: 0x61, B: 0x62, b: 0x62, C: 0x63, c: 0x63, D: 0x64, d: 0x64, E: 0x65, e: 0x65, F: 0x66, f: 0x66, G: 0x67, g: 0x67, H: 0x68, h: 0x68, I: 0x69, i: 0x69, J: 0x6a, j: 0x6a, K: 0x6b, k: 0x6b, L: 0x6c, l: 0x6c, M: 0x6d, m: 0x6d, N: 0x6e, n: 0x6e, O: 0x6f, o: 0x6f, P: 0x70, p: 0x70, Q: 0x71, q: 0x71, R: 0x72, r: 0x72, S: 0x73, s: 0x73, T: 0x74, t: 0x74, U: 0x75, u: 0x75, V: 0x76, v: 0x76, W: 0x77, w: 0x77, X: 0x78, x: 0x78, Y: 0x79, y: 0x79, Z: 0x7a, z: 0x7a, "!": 0x21, Exclaim: 0x21, '"': 0x22, DoubleQuote: 0x22, "#": 0x23, Hash: 0x23, "$": 0x24, Dollar: 0x24, "&": 0x26, Ampersand: 0x26, "'": 0x27, SingleQuote: 0x27, "(": 0x28, OpeningParenthesis: 0x28, ")": 0x29, ClosingParenthesis: 0x29, "*": 0x2a, Asterisk: 0x2A, "+": 0x2b, Plus: 0x2B, ",": 0x2c, Comma: 0x2C, "-": 0x2d, Minus: 0x2D, ".": 0x2e, Period: 0x2E, "/": 0x2f, Slash: 0x2F, ",": 0x3a, Colon: 0x3A, ";": 0x3b, SemiColon: 0x3B, "<": 0x3c, Less: 0x3C, "=": 0x3d, Equals: 0x3D, ">": 0x3e, Greater: 0x3E, "?": 0x3f, Question: 0x3F, "@": 0x40, At: 0x40, "[": 0x5b, OpeningBracket: 0x5B, "\\": 0x5c, BackSlash: 0x5C, "]": 0x5d, ClosingBracket: 0x5D, "^": 0x5e, Caret: 0x5E, "_": 0x5f, Underscore: 0x5F, "`": 0x60, BackQuote: 0x60, }; FunctionKeys = { Space: 0x20, Backspace: 0xFF08, Tab: 0xFF09, Linefeed: 0xFF0A, Clear: 0xFF0B, Return: 0xFF0D, Pause: 0xFF13, Sys_Req: 0xFF15, "Esc": 0xff1b, Escape: 0xFF1B, "Del": 0xffff, Delete: 0xFFFF, Home: 0xFF50, Left: 0xFF51, Up: 0xFF52, Right: 0xFF53, Down: 0xFF54, Prior: 0xFF55, Page_Up: 0xFF55, Next: 0xFF56, Page_Down: 0xFF56, End: 0xFF57, Begin: 0xFF58, Select: 0xFF60, Print: 0xFF61, Execute: 0xFF62, Insert: 0xFF63, Undo: 0xFF65, Redo: 0xFF66, Menu: 0xFF67, Find: 0xFF68, Cancel: 0xFF69, Help: 0xFF6A, Break: 0xFF6B, # Any key binding using these would be pretty confusing … Mode_switch: 0xFF7E, Script_switch: 0xFF7E, Num_Lock: 0xFF7F, Scroll_Lock: 0xFF14, # Keypad keys probably should not be used because their normal key equivalent will be passed by OSG AFAIK KP_Space: 0xFF80, KP_Tab: 0xFF89, KP_Enter: 0xFF8D, KP_F1: 0xFF91, KP_F2: 0xFF92, KP_F3: 0xFF93, KP_F4: 0xFF94, KP_Home: 0xFF95, KP_Left: 0xFF96, KP_Up: 0xFF97, KP_Right: 0xFF98, KP_Down: 0xFF99, KP_Prior: 0xFF9A, KP_Page_Up: 0xFF9A, KP_Next: 0xFF9B, KP_Page_Down: 0xFF9B, KP_End: 0xFF9C, KP_Begin: 0xFF9D, KP_Insert: 0xFF9E, KP_Delete: 0xFF9F, KP_Equal: 0xFFBD, KP_Multiply: 0xFFAA, KP_Add: 0xFFAB, KP_Separator: 0xFFAC, KP_Subtract: 0xFFAD, KP_Decimal: 0xFFAE, KP_Divide: 0xFFAF, KP_0: 0xFFB0, KP_1: 0xFFB1, KP_2: 0xFFB2, KP_3: 0xFFB3, KP_4: 0xFFB4, KP_5: 0xFFB5, KP_6: 0xFFB6, KP_7: 0xFFB7, KP_8: 0xFFB8, KP_9: 0xFFB9, F1: 0xFFBE, F2: 0xFFBF, F3: 0xFFC0, F4: 0xFFC1, F5: 0xFFC2, F6: 0xFFC3, F7: 0xFFC4, F8: 0xFFC5, F9: 0xFFC6, F10: 0xFFC7, F11: 0xFFC8, # most keyboards only have F1 - F11, so better not use any of the below FunctionKeys F12: 0xFFC9, F13: 0xFFCA, F14: 0xFFCB, F15: 0xFFCC, F16: 0xFFCD, F17: 0xFFCE, F18: 0xFFCF, F19: 0xFFD0, F20: 0xFFD1, F21: 0xFFD2, F22: 0xFFD3, F23: 0xFFD4, F24: 0xFFD5, F25: 0xFFD6, F26: 0xFFD7, F27: 0xFFD8, F28: 0xFFD9, F29: 0xFFDA, F30: 0xFFDB, F31: 0xFFDC, F32: 0xFFDD, F33: 0xFFDE, F34: 0xFFDF, F35: 0xFFE0, # Do not use the below keys ! Shift_L: 0xFFE1, Shift_R: 0xFFE2, Control_L: 0xFFE3, Control_R: 0xFFE4, Caps_Lock: 0xFFE5, Shift_Lock: 0xFFE6, Meta_L: 0xFFE7, Meta_R: 0xFFE8, Alt_L: 0xFFE9, Alt_R: 0xFFEA, #Super_L: 0xFFEB, #Super_R: 0xFFEC, #Hyper_L: 0xFFED, #Hyper_R: 0xFFEE, }; ModifierKeys = { Shift_L: 0x0001, Shift_R: 0x0002, Control_L: 0x0004, Control_R: 0x0008, Alt_L: 0x0010, Alt_R: 0x0020, Meta_L: 0x0040, Meta_R: 0x0080, # These do not seem to be available in OSG events #Super_L: 0x0100, #Super_R: 0x0200, #Hyper_L: 0x0400, #Hyper_R: 0x0800, #Num_Lock: 0x1000, Caps_Lock: 0x2000, }; # Only use the below keys ! ModifierKeys.Ctrl = (ModifierKeys.Control_L | ModifierKeys.Control_R); ModifierKeys.Shift = (ModifierKeys.Shift_L | ModifierKeys.Shift_R); ModifierKeys.Alt = (ModifierKeys.Alt_L | ModifierKeys.Alt_R); ModifierKeys.Meta = (ModifierKeys.Meta_L | ModifierKeys.Meta_R); # These do not seem to be available in OSG events #ModifierKeys.Super = (ModifierKeys.Super_L | ModifierKeys.Super_R); #ModifierKeys.Hyper = (ModifierKeys.Hyper_L | ModifierKeys.Hyper_R); var Shortcut = { new: func(modifiers, keys...) { var m = { parents: [Shortcut], modifiers: modifiers, keys: keys, }; if (m.modifiers == nil) { m.modifiers = []; m.keys = []; } elsif (isa(m.modifiers, Shortcut)) { var shortcut = m.modifiers; m.modifiers = shortcut.modifiers; m.keys = shortcut.keys; } elsif (typeof(m.modifiers) == "scalar") { var res = m.parseString(m.modifiers); if (typeof(res) == "scalar") { logprint(LOG_ALERT, "keyboard.Shortcut.parseString failed parsing '" ~ m.modifiers ~ "' with the following reason:"); logprint(LOG_ALERT, "\t" ~ res); m.modifiers = []; m.keys = []; } else { m.modifiers = res[0]; m.keys = res[1]; } } return m; }, parseString: func(s) { if (size(s) == 0) { return "shortcut string is empty"; } var found = []; var modifiers = []; var input = split("+", s); foreach (var key; input) { if (string.match(key, "<*>")) { append(modifiers, substr(key, 1, size(key) - 2)); } else { append(found, key); } } var newmodifiers = []; foreach (var mod; modifiers) { if (ModifierKeys[mod] == nil) { return "Unknown modifier key '" ~ mod ~ "' !"; } else { append(newmodifiers, ModifierKeys[mod]); } } var newfound = []; foreach (var key; found) { if (PrintableKeys[key] == nil) { if (FunctionKeys[key] == nil) { return "Unknown key '" ~ key ~ "' !"; } else { append(newfound, FunctionKeys[key]); } } else { append(newfound, PrintableKeys[key]); } } return [newmodifiers, newfound]; }, match: func(keys, shift=0, ctrl=0, alt=0, meta=0) { if (!me.modifiers and !me.keys) { return 0; } var match = 1; match &= contains(me.modifiers, ModifierKeys["Shift"]) == shift; match &= contains(me.modifiers, ModifierKeys["Ctrl"]) == ctrl; match &= contains(me.modifiers, ModifierKeys["Alt"]) == alt; match &= contains(me.modifiers, ModifierKeys["Meta"]) == meta; match &= size(keys) == size(me.keys); if (!match) { return 0; } for (var i = 0; i < size(keys); i += 1) { match &= keys[i] == me.keys[i]; } return match; }, repr: func { if (!me.modifiers and !me.keys) { return ""; } var names = []; foreach (var mod; me.modifiers) { append(names, findKeyName(mod)); } foreach (var key; me.keys) { append(names, findKeyName(key)); } return string.join(" + ", names); }, equals: func(other) { return me.modifiers == other.modifiers and me.keys == other.keys; }, }; var findKeyName = func(key) { foreach (var mod; keys(ModifierKeys)) { if (ModifierKeys[mod] == key) { return mod; } } foreach (var printable; keys(PrintableKeys)) { if (PrintableKeys[printable] == key) { return printable; } } foreach (var function; keys(FunctionKeys)) { if (FunctionKeys[function] == key) { return function; } } return nil; }; var namesToKeys = func(strings...) { var res = []; foreach (var s; strings) { var key = ModifierKeys[s] or PrintableKeys[s] or FunctionKeys[s]; if (!key) { logprint(LOG_ALERT, "keyboard.namesToKeys: unknown key '" ~ key ~ "' - skipping !"); continue; } append(res, key); } return res; }; var Binding = { new: func(shortcut, f) { var m = { parents: [Binding], f: f, shortcut: shortcut, }; return m; }, fire: func(e) { debug.dump(e); if (isfunc(me.f)) { me.f(e); } } };