diff --git a/_build_js.sh b/_build_js.sh index 21c1196..912feea 100755 --- a/_build_js.sh +++ b/_build_js.sh @@ -16,7 +16,6 @@ fi npm run babel -- -o "out/js/app.$FRONT_END_HASH.js" ${smarg} js/lib \ js/lib/chibi.js \ - js/lib/keymaster.js \ js/lib/polyfills.js \ js/event_emitter.js \ js/utils.js \ diff --git a/js/lib/keymaster.js b/js/lib/keymaster.js deleted file mode 100644 index 0f33d44..0000000 --- a/js/lib/keymaster.js +++ /dev/null @@ -1,311 +0,0 @@ -// keymaster.js -// (c) 2011-2013 Thomas Fuchs -// keymaster.js may be freely distributed under the MIT license. - -;(function(global){ - var k, - _handlers = {}, - _mods = { 16: false, 18: false, 17: false, 91: false }, - _scope = 'all', - // modifier keys - _MODIFIERS = { - '⇧': 16, shift: 16, - '⌥': 18, alt: 18, option: 18, - '⌃': 17, ctrl: 17, control: 17, - '⌘': 91, command: 91 - }, - // special keys - _MAP = { - backspace: 8, tab: 9, clear: 12, - enter: 13, 'return': 13, - esc: 27, escape: 27, space: 32, - left: 37, up: 38, - right: 39, down: 40, - del: 46, 'delete': 46, - home: 36, end: 35, - pageup: 33, pagedown: 34, - ',': 188, '.': 190, '/': 191, - '`': 192, '-': 189, '=': 187, - ';': 186, '\'': 222, - '[': 219, ']': 221, '\\': 220, - // added: - insert: 45, - np_0: 96, np_1: 97, np_2: 98, np_3: 99, np_4: 100, np_5: 101, - np_6: 102, np_7: 103, np_8: 104, np_9: 105, np_mul: 106, - np_add: 107, np_sub: 109, np_point: 110, np_div: 111, numlock: 144, - }, - code = function(x){ - return _MAP[x] || x.toUpperCase().charCodeAt(0); - }, - _downKeys = []; - - for(k=1;k<20;k++) _MAP['f'+k] = 111+k; - - // IE doesn't support Array#indexOf, so have a simple replacement - function index(array, item){ - var i = array.length; - while(i--) if(array[i]===item) return i; - return -1; - } - - // for comparing mods before unassignment - function compareArray(a1, a2) { - if (a1.length != a2.length) return false; - for (var i = 0; i < a1.length; i++) { - if (a1[i] !== a2[i]) return false; - } - return true; - } - - var modifierMap = { - 16:'shiftKey', - 18:'altKey', - 17:'ctrlKey', - 91:'metaKey' - }; - function updateModifierKey(event) { - for(k in _mods) _mods[k] = event[modifierMap[k]]; - }; - - function isModifierPressed(mod) { - if (mod=='control'||mod=='ctrl') return _mods[17]; - if (mod=='shift') return _mods[16]; - if (mod=='meta') return _mods[91]; - if (mod=='alt') return _mods[18]; - return false; - } - - // handle keydown event - function dispatch(event) { - var key, handler, k, i, modifiersMatch, scope; - key = event.keyCode; - - if (index(_downKeys, key) == -1) { - _downKeys.push(key); - } - - // if a modifier key, set the key. property to true and return - if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko - if(key in _mods) { - _mods[key] = true; - // 'assignKey' from inside this closure is exported to window.key - for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true; - return; - } - updateModifierKey(event); - - // see if we need to ignore the keypress (filter() can can be overridden) - // by default ignore key presses if a select, textarea, or input is focused - if(!assignKey.filter.call(this, event)) return; - - // abort if no potentially matching shortcuts found - if (!(key in _handlers)) return; - - scope = getScope(); - - // for each potential shortcut - for (i = 0; i < _handlers[key].length; i++) { - handler = _handlers[key][i]; - - // see if it's in the current scope - if(handler.scope == scope || handler.scope == 'all'){ - // check if modifiers match if any - modifiersMatch = handler.mods.length > 0; - for(k in _mods) - if((!_mods[k] && index(handler.mods, +k) > -1) || - (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false; - // call the handler and stop the event if neccessary - if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){ - if(handler.method(event, handler)===false){ - if(event.preventDefault) event.preventDefault(); - else event.returnValue = false; - if(event.stopPropagation) event.stopPropagation(); - if(event.cancelBubble) event.cancelBubble = true; - } - } - } - } - }; - - // unset modifier keys on keyup - function clearModifier(event){ - var key = event.keyCode, k, - i = index(_downKeys, key); - - // remove key from _downKeys - if (i >= 0) { - _downKeys.splice(i, 1); - } - - if(key == 93 || key == 224) key = 91; - if(key in _mods) { - _mods[key] = false; - for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false; - } - }; - - function resetModifiers() { - for(k in _mods) _mods[k] = false; - for(k in _MODIFIERS) assignKey[k] = false; - }; - - // parse and assign shortcut - function assignKey(key, scope, method){ - var keys, mods; - keys = getKeys(key); - if (method === undefined) { - method = scope; - scope = 'all'; - } - - // for each shortcut - for (var i = 0; i < keys.length; i++) { - // set modifier keys if any - mods = []; - key = keys[i].split('+'); - if (key.length > 1){ - mods = getMods(key); - key = [key[key.length-1]]; - } - // convert to keycode and... - key = key[0] - key = code(key); - // ...store handler - if (!(key in _handlers)) _handlers[key] = []; - _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods }); - } - }; - - // unbind all handlers for given key in current scope - function unbindKey(key, scope) { - var multipleKeys, keys, - mods = [], - i, j, obj; - - multipleKeys = getKeys(key); - - for (j = 0; j < multipleKeys.length; j++) { - keys = multipleKeys[j].split('+'); - - if (keys.length > 1) { - mods = getMods(keys); - } - - key = keys[keys.length - 1]; - key = code(key); - - if (scope === undefined) { - scope = getScope(); - } - if (!_handlers[key]) { - return; - } - for (i = 0; i < _handlers[key].length; i++) { - obj = _handlers[key][i]; - // only clear handlers if correct scope and mods match - if (obj.scope === scope && compareArray(obj.mods, mods)) { - _handlers[key][i] = {}; - } - } - } - }; - - // Returns true if the key with code 'keyCode' is currently down - // Converts strings into key codes. - function isPressed(keyCode) { - if (typeof(keyCode)=='string') { - keyCode = code(keyCode); - } - return index(_downKeys, keyCode) != -1; - } - - function getPressedKeyCodes() { - return _downKeys.slice(0); - } - - function filter(event){ - var tagName = (event.target || event.srcElement).tagName; - // ignore keypressed in any elements that support keyboard data input - return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA'); - } - - // initialize key. to false - for(k in _MODIFIERS) assignKey[k] = false; - - // set current scope (default 'all') - function setScope(scope){ _scope = scope || 'all' }; - function getScope(){ return _scope || 'all' }; - - // delete all handlers for a given scope - function deleteScope(scope){ - var key, handlers, i; - - for (key in _handlers) { - handlers = _handlers[key]; - for (i = 0; i < handlers.length; ) { - if (handlers[i].scope === scope) handlers.splice(i, 1); - else i++; - } - } - }; - - // abstract key logic for assign and unassign - function getKeys(key) { - var keys; - key = key.replace(/\s/g, ''); - keys = key.split(','); - if ((keys[keys.length - 1]) == '') { - keys[keys.length - 2] += ','; - } - return keys; - } - - // abstract mods logic for assign and unassign - function getMods(key) { - var mods = key.slice(0, key.length - 1); - for (var mi = 0; mi < mods.length; mi++) - mods[mi] = _MODIFIERS[mods[mi]]; - return mods; - } - - // cross-browser events - function addEvent(object, event, method) { - if (object.addEventListener) - object.addEventListener(event, method, false); - else if(object.attachEvent) - object.attachEvent('on'+event, function(){ method(window.event) }); - }; - - // set the handlers globally on document - addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48 - addEvent(document, 'keyup', clearModifier); - - // reset modifiers to false whenever the window is (re)focused. - addEvent(window, 'focus', resetModifiers); - - // store previously defined key - var previousKey = global.key; - - // restore previously defined key and return reference to our key object - function noConflict() { - var k = global.key; - global.key = previousKey; - return k; - } - - // set window.key and window.key.set/get/deleteScope, and the default filter - global.key = assignKey; - global.key.setScope = setScope; - global.key.getScope = getScope; - global.key.deleteScope = deleteScope; - global.key.filter = filter; - global.key.isPressed = isPressed; - global.key.isModifier = isModifierPressed; - global.key.getPressedKeyCodes = getPressedKeyCodes; - global.key.noConflict = noConflict; - global.key.unbind = unbindKey; - - if(typeof module !== 'undefined') module.exports = assignKey; - -})(window); - diff --git a/js/term.js b/js/term.js index e4d3b29..c7289d9 100644 --- a/js/term.js +++ b/js/term.js @@ -2,7 +2,7 @@ window.termInit = function ({ labels, theme, allFn }) { const screen = new TermScreen() const conn = new Conn(screen) - const input = Input(conn) + const input = Input(conn, screen) const termUpload = TermUpl(conn, input, screen) screen.input = input diff --git a/js/term_input.js b/js/term_input.js index 2199f7e..ad91ebd 100644 --- a/js/term_input.js +++ b/js/term_input.js @@ -14,7 +14,91 @@ * r - mb release * m - mouse move */ -window.Input = function (conn) { +window.Input = function (conn, screen) { + const KEY_NAMES = { + 0x03: 'Cancel', + 0x06: 'Help', + 0x08: 'Backspace', + 0x09: 'Tab', + 0x0C: 'Clear', + 0x0D: 'Enter', + 0x10: 'Shift', + 0x11: 'Control', + 0x12: 'Alt', + 0x13: 'Pause', + 0x14: 'CapsLock', + 0x1B: 'Escape', + 0x20: ' ', + 0x21: 'PageUp', + 0x22: 'PageDown', + 0x23: 'End', + 0x24: 'Home', + 0x25: 'ArrowLeft', + 0x26: 'ArrowUp', + 0x27: 'ArrowRight', + 0x28: 'ArrowDown', + 0x29: 'Select', + 0x2A: 'Print', + 0x2B: 'Execute', + 0x2C: 'PrintScreen', + 0x2D: 'Insert', + 0x2E: 'Delete', + 0x3A: ':', + 0x3B: ';', + 0x3C: '<', + 0x3D: '=', + 0x3E: '>', + 0x3F: '?', + 0x40: '@', + 0x5B: 'Meta', + 0x5C: 'Meta', + 0x5D: 'ContextMenu', + 0x6A: 'Numpad*', + 0x6B: 'Numpad+', + 0x6D: 'Numpad-', + 0x6E: 'Numpad.', + 0x6F: 'Numpad/', + 0x90: 'NumLock', + 0x91: 'ScrollLock', + 0xA0: '^', + 0xA1: '!', + 0xA2: '"', + 0xA3: '#', + 0xA4: '$', + 0xA5: '%', + 0xA6: '&', + 0xA7: '_', + 0xA8: '(', + 0xA9: ')', + 0xAA: '*', + 0xAB: '+', + 0xAC: '|', + 0xAD: '-', + 0xAE: '{', + 0xAF: '}', + 0xB0: '~', + 0xBA: ';', + 0xBB: '=', + 0xBC: 'Numpad,', + 0xBD: '-', + 0xBE: 'Numpad,', + 0xC0: '`', + 0xC2: 'Numpad,', + 0xDB: '[', + 0xDC: '\\', + 0xDD: ']', + 0xDE: "'", + 0xE0: 'Meta' + } + // numbers 0-9 + for (let i = 0x30; i <= 0x39; i++) KEY_NAMES[i] = String.fromCharCode(i) + // characters A-Z + for (let i = 0x41; i <= 0x5A; i++) KEY_NAMES[i] = String.fromCharCode(i) + // function F1-F20 + for (let i = 0x70; i <= 0x83; i++) KEY_NAMES[i] = `F${i - 0x70 + 1}` + // numpad 0-9 + for (let i = 0x60; i <= 0x69; i++) KEY_NAMES[i] = `Numpad${i - 0x60}` + let cfg = { np_alt: false, cu_alt: false, @@ -22,122 +106,164 @@ window.Input = function (conn) { mt_click: false, mt_move: false, no_keys: false, - crlf_mode: false + crlf_mode: false, + all_fn: false + } + + /** Fn alt choice for key message */ + const fa = (alt, normal) => cfg.fn_alt ? alt : normal + + /** Cursor alt choice for key message */ + const ca = (alt, normal) => cfg.cu_alt ? alt : normal + + /** Numpad alt choice for key message */ + const na = (alt, normal) => cfg.np_alt ? alt : normal + + const keymap = { + /* eslint-disable key-spacing */ + 'Backspace': '\x08', + 'Tab': '\x09', + 'Enter': () => cfg.crlf_mode ? '\x0d\x0a' : '\x0d', + 'Control+Enter': '\x0a', + 'Escape': '\x1b', + 'ArrowUp': () => ca('\x1bOA', '\x1b[A'), + 'ArrowDown': () => ca('\x1bOB', '\x1b[B'), + 'ArrowRight': () => ca('\x1bOC', '\x1b[C'), + 'ArrowLeft': () => ca('\x1bOD', '\x1b[D'), + 'Home': () => ca('\x1bOH', fa('\x1b[H', '\x1b[1~')), + 'Insert': '\x1b[2~', + 'Delete': '\x1b[3~', + 'End': () => ca('\x1bOF', fa('\x1b[F', '\x1b[4~')), + 'PageUp': '\x1b[5~', + 'PageDown': '\x1b[6~', + 'F1': () => fa('\x1bOP', '\x1b[11~'), + 'F2': () => fa('\x1bOQ', '\x1b[12~'), + 'F3': () => fa('\x1bOR', '\x1b[13~'), + 'F4': () => fa('\x1bOS', '\x1b[14~'), + 'F5': '\x1b[15~', // note the disconnect + 'F6': '\x1b[17~', + 'F7': '\x1b[18~', + 'F8': '\x1b[19~', + 'F9': '\x1b[20~', + 'F10': '\x1b[21~', // note the disconnect + 'F11': '\x1b[23~', + 'F12': '\x1b[24~', + 'Shift+F1': () => fa('\x1bO1;2P', '\x1b[25~'), + 'Shift+F2': () => fa('\x1bO1;2Q', '\x1b[26~'), // note the disconnect + 'Shift+F3': () => fa('\x1bO1;2R', '\x1b[28~'), + 'Shift+F4': () => fa('\x1bO1;2S', '\x1b[29~'), // note the disconnect + 'Shift+F5': () => fa('\x1b[15;2~', '\x1b[31~'), + 'Shift+F6': () => fa('\x1b[17;2~', '\x1b[32~'), + 'Shift+F7': () => fa('\x1b[18;2~', '\x1b[33~'), + 'Shift+F8': () => fa('\x1b[19;2~', '\x1b[34~'), + 'Shift+F9': () => fa('\x1b[20;2~', '\x1b[35~'), // 35-38 are not standard - but what is? + 'Shift+F10': () => fa('\x1b[21;2~', '\x1b[36~'), + 'Shift+F11': () => fa('\x1b[22;2~', '\x1b[37~'), + 'Shift+F12': () => fa('\x1b[23;2~', '\x1b[38~'), + 'Numpad0': () => na('\x1bOp', '0'), + 'Numpad1': () => na('\x1bOq', '1'), + 'Numpad2': () => na('\x1bOr', '2'), + 'Numpad3': () => na('\x1bOs', '3'), + 'Numpad4': () => na('\x1bOt', '4'), + 'Numpad5': () => na('\x1bOu', '5'), + 'Numpad6': () => na('\x1bOv', '6'), + 'Numpad7': () => na('\x1bOw', '7'), + 'Numpad8': () => na('\x1bOx', '8'), + 'Numpad9': () => na('\x1bOy', '9'), + 'Numpad*': () => na('\x1bOR', '*'), + 'Numpad+': () => na('\x1bOl', '+'), + 'Numpad-': () => na('\x1bOS', '-'), + 'Numpad.': () => na('\x1bOn', '.'), + 'Numpad/': () => na('\x1bOQ', '/'), + // we don't implement numlock key (should change in numpad_alt mode, + // but it's even more useless than the rest and also has the side + // effect of changing the user's numlock state) + + // shortcuts + 'Control+]': '\x1b', // alternate way to enter ESC + 'Control+\\': '\x1c', + 'Control+[': '\x1d', + 'Control+^': '\x1e', + 'Control+_': '\x1f', + + // extra controls + 'Control+ArrowLeft': '\x1f[1;5D', + 'Control+ArrowRight': '\x1f[1;5C', + 'Control+ArrowUp': '\x1f[1;5A', + 'Control+ArrowDown': '\x1f[1;5B', + 'Control+Home': '\x1f[1;5H', + 'Control+End': '\x1f[1;5F', + + // extra shift controls + 'Shift+ArrowLeft': '\x1f[1;2D', + 'Shift+ArrowRight': '\x1f[1;2C', + 'Shift+ArrowUp': '\x1f[1;2A', + 'Shift+ArrowDown': '\x1f[1;2B', + 'Shift+Home': '\x1f[1;2H', + 'Shift+End': '\x1f[1;2F', + + // macOS text editing commands + 'Alt+ArrowLeft': '\x1bb', // ⌥← to go back a word (^[b) + 'Alt+ArrowRight': '\x1bf', // ⌥→ to go forward one word (^[f) + 'Meta+ArrowLeft': '\x01', // ⌘← to go to the beginning of a line (^A) + 'Meta+ArrowRight': '\x05', // ⌘→ to go to the end of a line (^E) + 'Alt+Backspace': '\x17', // ⌥⌫ to delete a word (^W) + 'Meta+Backspace': '\x15' // ⌘⌫ to delete to the beginning of a line (^U) + /* eslint-enable key-spacing */ } + // ctrl+[A-Z] sent as simple low ASCII codes + for (let i = 1; i <= 26; i++) keymap[`Control+${String.fromCharCode(0x40 + i)}`] = String.fromCharCode(i) + /** Send a literal message */ - function sendStrMsg (str) { + function sendString (str) { return conn.send('s' + str) } /** Send a button event */ - function sendBtnMsg (n) { + function sendButton (n) { conn.send('b' + String.fromCharCode(n)) } - /** Fn alt choice for key message */ - function fa (alt, normal) { - return cfg.fn_alt ? alt : normal - } + const keyBlacklist = [ + 'F5', 'F11', 'F12', 'Shift+F5' + ] - /** Cursor alt choice for key message */ - function ca (alt, normal) { - return cfg.cu_alt ? alt : normal - } + const handleKeyDown = function (e) { + if (cfg.no_keys) return - /** Numpad alt choice for key message */ - function na (alt, normal) { - return cfg.np_alt ? alt : normal - } + let modifiers = [] + // sorted alphabetically + if (e.altKey) modifiers.push('Alt') + if (e.ctrlKey) modifiers.push('Control') + if (e.metaKey) modifiers.push('Meta') + if (e.shiftKey) modifiers.push('Shift') - function bindFnKeys (allFn) { - const keymap = { - 'tab': '\x09', - 'backspace': '\x08', - 'enter': cfg.crlf_mode ? '\x0d\x0a' : '\x0d', - 'ctrl+enter': '\x0a', - 'esc': '\x1b', - 'up': ca('\x1bOA', '\x1b[A'), - 'down': ca('\x1bOB', '\x1b[B'), - 'right': ca('\x1bOC', '\x1b[C'), - 'left': ca('\x1bOD', '\x1b[D'), - 'home': ca('\x1bOH', fa('\x1b[H', '\x1b[1~')), - 'insert': '\x1b[2~', - 'delete': '\x1b[3~', - 'end': ca('\x1bOF', fa('\x1b[F', '\x1b[4~')), - 'pageup': '\x1b[5~', - 'pagedown': '\x1b[6~', - 'f1': fa('\x1bOP', '\x1b[11~'), - 'f2': fa('\x1bOQ', '\x1b[12~'), - 'f3': fa('\x1bOR', '\x1b[13~'), - 'f4': fa('\x1bOS', '\x1b[14~'), - 'f5': '\x1b[15~', // note the disconnect - 'f6': '\x1b[17~', - 'f7': '\x1b[18~', - 'f8': '\x1b[19~', - 'f9': '\x1b[20~', - 'f10': '\x1b[21~', // note the disconnect - 'f11': '\x1b[23~', - 'f12': '\x1b[24~', - 'shift+f1': fa('\x1bO1;2P', '\x1b[25~'), - 'shift+f2': fa('\x1bO1;2Q', '\x1b[26~'), // note the disconnect - 'shift+f3': fa('\x1bO1;2R', '\x1b[28~'), - 'shift+f4': fa('\x1bO1;2S', '\x1b[29~'), // note the disconnect - 'shift+f5': fa('\x1b[15;2~', '\x1b[31~'), - 'shift+f6': fa('\x1b[17;2~', '\x1b[32~'), - 'shift+f7': fa('\x1b[18;2~', '\x1b[33~'), - 'shift+f8': fa('\x1b[19;2~', '\x1b[34~'), - 'shift+f9': fa('\x1b[20;2~', '\x1b[35~'), // 35-38 are not standard - but what is? - 'shift+f10': fa('\x1b[21;2~', '\x1b[36~'), - 'shift+f11': fa('\x1b[22;2~', '\x1b[37~'), - 'shift+f12': fa('\x1b[23;2~', '\x1b[38~'), - 'np_0': na('\x1bOp', '0'), - 'np_1': na('\x1bOq', '1'), - 'np_2': na('\x1bOr', '2'), - 'np_3': na('\x1bOs', '3'), - 'np_4': na('\x1bOt', '4'), - 'np_5': na('\x1bOu', '5'), - 'np_6': na('\x1bOv', '6'), - 'np_7': na('\x1bOw', '7'), - 'np_8': na('\x1bOx', '8'), - 'np_9': na('\x1bOy', '9'), - 'np_mul': na('\x1bOR', '*'), - 'np_add': na('\x1bOl', '+'), - 'np_sub': na('\x1bOS', '-'), - 'np_point': na('\x1bOn', '.'), - 'np_div': na('\x1bOQ', '/') - // we don't implement numlock key (should change in numpad_alt mode, - // but it's even more useless than the rest and also has the side - // effect of changing the user's numlock state) - } + let key = KEY_NAMES[e.which] || e.key - const blacklist = [ - 'f5', 'f11', 'f12', 'shift+f5' - ] + // ignore clipboard events + if ((e.ctrlKey || e.metaKey) && key === 'V') return - for (let k in keymap) { - if (!allFn && blacklist.includes(k)) continue - if (keymap.hasOwnProperty(k)) { - bind(k, keymap[k]) - } - } - } + let binding = null - /** Bind a keystroke to message */ - function bind (combo, str) { - // mac fix - allow also cmd - if (combo.indexOf('ctrl+') !== -1) { - combo += ',' + combo.replace('ctrl', 'command') - } + for (let name in keymap) { + let itemModifiers = name.split('+') + let itemKey = itemModifiers.pop() - // unbind possible old binding - key.unbind(combo) + if (itemKey === key && itemModifiers.sort().join() === modifiers.join()) { + if (keyBlacklist.includes(name) && !cfg.all_fn) continue + binding = keymap[name] + break + } + } - key(combo, function (e) { - if (cfg.no_keys) return + if (binding) { + if (binding instanceof Function) binding = binding() e.preventDefault() - sendStrMsg(str) - }) + if (typeof binding === 'string') { + sendString(binding) + } + } } /** Bind/rebind key messages */ @@ -145,54 +271,36 @@ window.Input = function (conn) { // This takes care of text characters typed window.addEventListener('keypress', function (evt) { if (cfg.no_keys) return + if (evt.ctrlKey || evt.metaKey) return + let str = '' - if (evt.key) str = evt.key - else if (evt.which) str = String.fromCodePoint(evt.which) + if (evt.key && evt.key.length === 1) str = evt.key + else if (evt.which && evt.which !== 229) str = String.fromCodePoint(evt.which) + if (str.length > 0 && str.charCodeAt(0) >= 32) { - // console.log("Typed ", str); // prevent space from scrolling if (evt.which === 32) evt.preventDefault() - sendStrMsg(str) + sendString(str) } }) - // ctrl-letter codes are sent as simple low ASCII codes - for (let i = 1; i <= 26; i++) { - bind('ctrl+' + String.fromCharCode(96 + i), String.fromCharCode(i)) - } - /* eslint-disable */ - bind('ctrl+]', '\x1b') // alternate way to enter ESC - bind('ctrl+\\', '\x1c') - bind('ctrl+[', '\x1d') - bind('ctrl+^', '\x1e') - bind('ctrl+_', '\x1f') - - // extra ctrl- - bind('ctrl+left', '\x1f[1;5D') - bind('ctrl+right', '\x1f[1;5C') - bind('ctrl+up', '\x1f[1;5A') - bind('ctrl+down', '\x1f[1;5B') - bind('ctrl+home', '\x1f[1;5H') - bind('ctrl+end', '\x1f[1;5F') - - // extra shift- - bind('shift+left', '\x1f[1;2D') - bind('shift+right', '\x1f[1;2C') - bind('shift+up', '\x1f[1;2A') - bind('shift+down', '\x1f[1;2B') - bind('shift+home', '\x1f[1;2H') - bind('shift+end', '\x1f[1;2F') - - // macOS editing commands - bind('⌥+left', '\x1bb') // ⌥← to go back a word (^[b) - bind('⌥+right', '\x1bf') // ⌥→ to go forward one word (^[f) - bind('⌘+left', '\x01') // ⌘← to go to the beginning of a line (^A) - bind('⌘+right', '\x05') // ⌘→ to go to the end of a line (^E) - bind('⌥+backspace', '\x17') // ⌥⌫ to delete a word (^W) - bind('⌘+backspace', '\x15') // ⌘⌫ to delete to the beginning of a line (^U) - /* eslint-enable */ - - bindFnKeys(allFn) + window.addEventListener('keydown', handleKeyDown) + window.addEventListener('copy', e => { + let selectedText = screen.getSelectedText() + if (selectedText) { + e.preventDefault() + e.clipboardData.setData('text/plain', selectedText) + } + }) + window.addEventListener('paste', e => { + e.preventDefault() + console.log('User pasted:\n' + e.clipboardData.getData('text/plain')) + + // just write it for now + sendString(e.clipboardData.getData('text/plain')) + }) + + cfg.all_fn = allFn } // mouse button states @@ -207,7 +315,7 @@ window.Input = function (conn) { // Button presses $('#action-buttons button').forEach(s => { s.addEventListener('click', function (evt) { - sendBtnMsg(+this.dataset['n']) + sendButton(+this.dataset['n']) }) }) @@ -225,12 +333,27 @@ window.Input = function (conn) { }) } + // record modifier keys + // bits: Meta, Alt, Shift, Ctrl + let modifiers = 0b0000 + + window.addEventListener('keydown', e => { + if (e.ctrlKey) modifiers |= 1 + if (e.shiftKey) modifiers |= 2 + if (e.altKey) modifiers |= 4 + if (e.metaKey) modifiers |= 8 + }) + window.addEventListener('keyup', e => { + modifiers = 0 + if (e.ctrlKey) modifiers |= 1 + if (e.shiftKey) modifiers |= 2 + if (e.altKey) modifiers |= 4 + if (e.metaKey) modifiers |= 8 + }) + /** Prepare modifiers byte for mouse message */ function packModifiersForMouse () { - return (key.isModifier('ctrl') ? 1 : 0) | - (key.isModifier('shift') ? 2 : 0) | - (key.isModifier('alt') ? 4 : 0) | - (key.isModifier('meta') ? 8 : 0) + return modifiers } return { @@ -238,7 +361,7 @@ window.Input = function (conn) { init, /** Send a literal string message */ - sendString: sendStrMsg, + sendString, /** Enable alternate key modes (cursors, numpad, fn) */ setAlts: function (cu, np, fn, crlf) { diff --git a/js/term_screen.js b/js/term_screen.js index 44d87d6..8b9635e 100644 --- a/js/term_screen.js +++ b/js/term_screen.js @@ -344,12 +344,6 @@ window.TermScreen = class TermScreen extends EventEmitter { } selectEnd(e.offsetX, e.offsetY) }) - - // bind ctrl+shift+c to copy - key('⌃+⇧+c', e => { - e.preventDefault() - this.copySelectionToClipboard() - }) } /**