From 3551ebd9a17cf150d95555ba3ff234910e28a427 Mon Sep 17 00:00:00 2001 From: Devaev Maxim Date: Wed, 8 Aug 2018 08:38:47 +0300 Subject: paste-as-keys --- kvmd/eslintrc.yaml | 1 + kvmd/web/css/leds.css | 3 +- kvmd/web/index.html | 3 + kvmd/web/js/hid.js | 130 +++++++++++++++++++++++++++++++++++---- kvmd/web/js/session.js | 4 +- kvmd/web/svg/gear-led.svg | 151 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 276 insertions(+), 16 deletions(-) create mode 100644 kvmd/web/svg/gear-led.svg (limited to 'kvmd') diff --git a/kvmd/eslintrc.yaml b/kvmd/eslintrc.yaml index 68d1faf6..215105b0 100644 --- a/kvmd/eslintrc.yaml +++ b/kvmd/eslintrc.yaml @@ -12,6 +12,7 @@ globals: env: browser: true + es6: true extends: "eslint:recommended" diff --git a/kvmd/web/css/leds.css b/kvmd/web/css/leds.css index 0dbe6fe1..6558eb35 100644 --- a/kvmd/web/css/leds.css +++ b/kvmd/web/css/leds.css @@ -25,7 +25,8 @@ img.led-hdd-busy { filter: invert(0.5) sepia(1) saturate(15) hue-rotate(320deg); } -img.led-msd-writing { +img.led-msd-writing, +img.led-pak-typing { -webkit-filter: invert(0.5) sepia(1) saturate(5) hue-rotate(0deg); filter: invert(0.5) sepia(1) saturate(5) hue-rotate(0deg); -webkit-animation: spin 2s linear infinite; diff --git a/kvmd/web/index.html b/kvmd/web/index.html index 9d87e392..0473bd4d 100644 --- a/kvmd/web/index.html +++ b/kvmd/web/index.html @@ -189,9 +189,12 @@
  • + Shortcuts ↴
    + +

    diff --git a/kvmd/web/js/hid.js b/kvmd/web/js/hid.js index 130b66a8..86d3931d 100644 --- a/kvmd/web/js/hid.js +++ b/kvmd/web/js/hid.js @@ -1,7 +1,24 @@ var hid = new function() { + var __ws = null; + var __chars_to_codes = {}; + var __codes_delay = 50; + this.init = function() { keyboard.init(); mouse.init(); + if (window.navigator.clipboard && window.navigator.clipboard.readText) { + __chars_to_codes = __buildCharsToCodes(); + $("pak-button").onclick = __pasteAsKeys; + } else { + $("pak-button").title = "Your browser does not support the Clipboard API.\nUse Google Chrome or Chromium."; + } + }; + + this.setSocket = function(ws) { + __ws = ws; + keyboard.setSocket(ws); + mouse.setSocket(ws); + $("pak-button").disabled = !(window.navigator.clipboard && window.navigator.clipboard.readText && ws); }; this.updateLeds = function() { @@ -14,24 +31,111 @@ var hid = new function() { }; this.emitShortcut = function(...codes) { - tools.debug("Emitted keys:", codes); - var delay = 0; - [[codes, true], [codes.slice().reverse(), false]].forEach(function(op) { - var [op_codes, state] = op; - op_codes.forEach(function(code) { - setTimeout(() => keyboard.fireEvent(code, state), delay); - delay += 100; + return new Promise(function(resolve) { + tools.debug("Emitting keys:", codes); + + var raw_events = []; + [[codes, true], [codes.slice().reverse(), false]].forEach(function(op) { + var [op_codes, state] = op; + op_codes.forEach(function(code) { + raw_events.push({code: code, state: state}); + }); }); + + var index = 0; + var iterate = () => setTimeout(function() { + keyboard.fireEvent(raw_events[index].code, raw_events[index].state); + ++index; + if (index < raw_events.length) { + iterate(); + } else { + resolve(null); + } + }, __codes_delay); + iterate(); }); }; - this.installCapture = function(ws) { - keyboard.setSocket(ws); - mouse.setSocket(ws); + var __buildCharsToCodes = function() { + var chars_to_codes = { + "\n": ["Enter"], + "\t": ["Tab"], + " ": ["Space"], + "`": ["Backquote"], "~": ["ShiftLeft", "Backquote"], + "\\": ["Backslash"], "|": ["ShiftLeft", "Backslash"], + "[": ["BracketLeft"], "{": ["ShiftLeft", "BracketLeft"], + "]": ["BracketLeft"], "}": ["ShiftLeft", "BracketRight"], + ",": ["Comma"], "<": ["ShiftLeft", "Comma"], + ".": ["Period"], ">": ["ShiftLeft", "Period"], + "1": ["Digit1"], "!": ["ShiftLeft", "Digit1"], + "2": ["Digit2"], "@": ["ShiftLeft", "Digit2"], + "3": ["Digit3"], "#": ["ShiftLeft", "Digit3"], + "4": ["Digit4"], "$": ["ShiftLeft", "Digit4"], + "5": ["Digit5"], "%": ["ShiftLeft", "Digit5"], + "6": ["Digit6"], "^": ["ShiftLeft", "Digit6"], + "7": ["Digit7"], "&": ["ShiftLeft", "Digit7"], + "8": ["Digit8"], "*": ["ShiftLeft", "Digit8"], + "9": ["Digit9"], "(": ["ShiftLeft", "Digit9"], + "0": ["Digit0"], ")": ["ShiftLeft", "Digit0"], + "-": ["Minus"], "_": ["ShiftLeft", "Minus"], + "'": ["Quote"], "\"": ["ShiftLeft", "Quote"], + ";": ["Semicolon"], ":": ["ShiftLeft", "Semicolon"], + "/": ["Slash"], "?": ["ShiftLeft", "Slash"], + "=": ["Equal"], "+": ["ShiftLeft", "Equal"], + }; + + for (var ch = "a".charCodeAt(0); ch <= "z".charCodeAt(0); ++ch) { + var low = String.fromCharCode(ch); + var up = low.toUpperCase(); + var code = "Key" + up; + chars_to_codes[low] = [code]; + chars_to_codes[up] = ["ShiftLeft", code]; + } + + return chars_to_codes; }; - this.clearCapture = function() { - mouse.setSocket(null); - keyboard.setSocket(null); + var __pasteAsKeys = function() { + window.navigator.clipboard.readText().then(function(clipboard_text) { + clipboard_text = clipboard_text.replace(/[^\x00-\x7F]/g, ""); // eslint-disable-line no-control-regex + if (clipboard_text) { + var clipboard_codes = []; + var codes_count = 0; + [...clipboard_text].forEach(function(ch) { + var codes = __chars_to_codes[ch]; + if (codes) { + codes_count += codes.length; + clipboard_codes.push(codes); + } + }); + + var confirm_msg = ( + "You are going to automatically type " + codes_count + + " characters from the system clipboard.\nAre you sure you want to continue?\n" + + "It will take " + (__codes_delay * codes_count * 2 / 1000) + " seconds." + ); + + if (codes_count <= 256 || confirm(confirm_msg)) { + $("pak-button").disabled = true; + $("pak-led").className = "led-pak-typing"; + + tools.debug("Paste-as-keys:", clipboard_text); + + var index = 0; + var iterate = function() { + hid.emitShortcut(...clipboard_codes[index]).then(function() { + ++index; + if (index < clipboard_codes.length && __ws) { + iterate(); + } else { + $("pak-button").disabled = false; + $("pak-led").className = "led-off"; + } + }); + }; + iterate(); + } + } + }); }; }; diff --git a/kvmd/web/js/session.js b/kvmd/web/js/session.js index d1d301bd..238ff877 100644 --- a/kvmd/web/js/session.js +++ b/kvmd/web/js/session.js @@ -32,7 +32,7 @@ var session = new function() { tools.debug("WebSocket opened:", event); atx.loadInitialState(); msd.loadInitialState(); - hid.installCapture(__ws); + hid.setSocket(__ws); __missed_heartbeats = 0; __ping_timer = setInterval(__pingServer, 1000); }; @@ -69,7 +69,7 @@ var session = new function() { clearInterval(__ping_timer); __ping_timer = null; } - hid.clearCapture(); + hid.setSocket(null); atx.clearState(); __ws = null; setTimeout(session.startPoller, 1000); diff --git a/kvmd/web/svg/gear-led.svg b/kvmd/web/svg/gear-led.svg new file mode 100644 index 00000000..db9aff21 --- /dev/null +++ b/kvmd/web/svg/gear-led.svg @@ -0,0 +1,151 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3