diff options
-rw-r--r-- | kvmd/eslintrc.yaml | 1 | ||||
-rw-r--r-- | kvmd/web/css/leds.css | 3 | ||||
-rw-r--r-- | kvmd/web/index.html | 3 | ||||
-rw-r--r-- | kvmd/web/js/hid.js | 130 | ||||
-rw-r--r-- | kvmd/web/js/session.js | 4 | ||||
-rw-r--r-- | kvmd/web/svg/gear-led.svg | 151 |
6 files changed, 276 insertions, 16 deletions
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 @@ <li class="ctl-right-actions"> <div class="ctl-dropdown"> <a class="ctl-item" href="#"> + <img data-dont-hide-menu id="pak-led" class="led-off" src="svg/gear-led.svg" /> Shortcuts ↴ </a> <div class="ctl-dropdown-content"> + <button disabled id="pak-button">• Paste-as-Keys <sup><i>ascii-only</i></sup></button> + <hr> <button onclick="hid.emitShortcut('ControlLeft', 'AltLeft', 'Delete');">• Ctrl+Alt+Del</button> <hr> <button onclick="hid.emitShortcut('ControlLeft', 'KeyW');">• Ctrl+W</button> 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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_1" + x="0px" + y="0px" + viewBox="0 0 521.99903 521.99702" + xml:space="preserve" + sodipodi:docname="gear-led-2.svg" + inkscape:version="0.92.2 2405546, 2018-03-11" + width="521.99902" + height="521.99701"><metadata + id="metadata49"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs47"> + +</defs><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="1020" + id="namedview45" + showgrid="false" + inkscape:zoom="0.92187681" + inkscape:cx="199.23524" + inkscape:cy="241.2828" + inkscape:window-x="0" + inkscape:window-y="30" + inkscape:window-maximized="1" + inkscape:current-layer="Layer_1" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" /> + + + + + + + + + + + + + + + + +<g + id="g4810"><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g4"> + <path + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 489.175,206.556 c -9.629,-1.442 -19.514,-2.825 -29.379,-4.111 -1.195,-0.155 -2.165,-0.966 -2.467,-2.064 -4.553,-16.523 -11.16,-32.467 -19.636,-47.389 -0.57,-1.002 -0.463,-2.266 0.273,-3.223 6.067,-7.885 12.081,-15.856 17.876,-23.69 7.824,-10.578 6.688,-25.588 -2.64,-34.917 L 420.836,58.796 c -9.329,-9.328 -24.338,-10.464 -34.918,-2.638 -7.817,5.782 -15.787,11.796 -23.689,17.875 -0.954,0.736 -2.221,0.843 -3.223,0.274 -14.921,-8.476 -30.865,-15.083 -47.389,-19.637 -1.099,-0.301 -1.91,-1.271 -2.066,-2.469 -1.289,-9.88 -2.671,-19.764 -4.109,-29.376 C 303.495,9.812 292.079,0 278.886,0 h -45.773 c -13.194,0 -24.61,9.812 -26.554,22.824 -1.439,9.614 -2.821,19.497 -4.11,29.379 -0.157,1.197 -0.967,2.165 -2.067,2.467 -16.524,4.556 -32.469,11.162 -47.387,19.637 -1.003,0.569 -2.269,0.459 -3.225,-0.274 C 141.869,67.954 133.898,61.94 126.08,56.157 115.499,48.332 100.49,49.47 91.163,58.797 L 58.797,91.163 c -9.329,9.33 -10.464,24.341 -2.638,34.918 5.804,7.846 11.818,15.815 17.875,23.688 0.735,0.955 0.843,2.22 0.274,3.223 -8.478,14.925 -15.084,30.869 -19.637,47.389 -0.301,1.097 -1.271,1.908 -2.467,2.065 -9.86,1.287 -19.744,2.669 -29.378,4.111 C 9.812,208.502 0,219.92 0,233.112 v 45.774 c 0,13.193 9.812,24.61 22.824,26.556 9.634,1.442 19.519,2.824 29.379,4.11 1.197,0.157 2.165,0.967 2.467,2.066 4.553,16.521 11.16,32.465 19.637,47.389 0.569,1.003 0.461,2.268 -0.274,3.223 -6.072,7.892 -12.086,15.862 -17.875,23.689 -7.825,10.578 -6.691,25.589 2.638,34.918 l 32.366,32.366 c 9.33,9.329 24.341,10.465 34.918,2.638 7.817,-5.782 15.787,-11.796 23.689,-17.875 0.955,-0.736 2.221,-0.842 3.223,-0.274 14.92,8.476 30.863,15.081 47.389,19.637 1.099,0.302 1.91,1.271 2.066,2.467 1.289,9.88 2.672,19.765 4.11,29.376 1.946,13.013 13.362,22.825 26.556,22.825 h 45.773 c 13.193,0 24.61,-9.812 26.555,-22.827 1.439,-9.623 2.821,-19.507 4.109,-29.376 0.157,-1.197 0.967,-2.166 2.066,-2.469 16.524,-4.556 32.469,-11.162 47.388,-19.637 1.003,-0.567 2.268,-0.459 3.224,0.274 7.901,6.079 15.872,12.093 23.689,17.875 10.578,7.825 25.588,6.691 34.918,-2.638 l 32.366,-32.366 c 9.328,-9.329 10.464,-24.339 2.639,-34.918 -5.795,-7.831 -11.81,-15.802 -17.876,-23.689 -0.735,-0.955 -0.843,-2.22 -0.273,-3.223 8.477,-14.924 15.083,-30.868 19.636,-47.388 0.304,-1.1 1.272,-1.91 2.469,-2.067 9.863,-1.286 19.748,-2.669 29.378,-4.11 13.013,-1.945 22.825,-13.362 22.825,-26.555 v -45.774 c 0,-13.19 -9.812,-24.608 -22.824,-26.553 z m -1.084,72.332 c 0,1.45 -1.054,2.7 -2.453,2.911 -9.482,1.419 -19.216,2.779 -28.932,4.048 -10.758,1.402 -19.56,9.024 -22.426,19.42 -4.029,14.618 -9.875,28.727 -17.375,41.932 -5.333,9.389 -4.504,21.012 2.112,29.612 5.976,7.768 11.899,15.617 17.604,23.329 0.842,1.137 0.702,2.769 -0.323,3.794 L 403.931,436.3 c -1.026,1.026 -2.657,1.163 -3.793,0.324 -7.697,-5.695 -15.548,-11.618 -23.33,-17.605 -8.599,-6.617 -20.221,-7.446 -29.609,-2.114 -13.205,7.5 -27.314,13.347 -41.934,17.377 -10.394,2.865 -18.016,11.667 -19.421,22.426 -1.267,9.722 -2.629,19.456 -4.047,28.932 -0.209,1.399 -1.461,2.453 -2.911,2.453 h -45.773 c -1.45,0 -2.702,-1.054 -2.911,-2.454 -1.415,-9.465 -2.778,-19.199 -4.047,-28.93 -1.403,-10.759 -9.027,-19.561 -19.421,-22.426 -14.621,-4.03 -28.73,-9.877 -41.934,-17.378 -4.117,-2.337 -8.664,-3.491 -13.196,-3.491 -5.804,0 -11.585,1.89 -16.412,5.607 -7.783,5.987 -15.633,11.91 -23.33,17.605 -1.138,0.839 -2.767,0.702 -3.792,-0.324 L 75.703,403.936 c -1.026,-1.026 -1.166,-2.656 -0.324,-3.793 5.701,-7.707 11.623,-15.556 17.604,-23.33 6.615,-8.6 7.445,-20.221 2.114,-29.609 C 87.594,333.995 81.749,319.887 77.72,305.27 74.855,294.876 66.053,287.253 55.295,285.85 c -9.712,-1.267 -19.447,-2.63 -28.934,-4.048 -1.399,-0.21 -2.453,-1.461 -2.453,-2.911 v -45.774 c 0,-1.45 1.054,-2.701 2.453,-2.911 9.487,-1.419 19.221,-2.781 28.932,-4.048 10.759,-1.402 19.561,-9.025 22.426,-19.42 4.027,-14.616 9.874,-28.725 17.377,-41.934 5.332,-9.389 4.502,-21.011 -2.113,-29.609 -5.965,-7.756 -11.888,-15.604 -17.604,-23.33 -0.84,-1.137 -0.701,-2.769 0.324,-3.793 l 32.365,-32.367 c 1.024,-1.026 2.655,-1.163 3.792,-0.324 7.697,5.694 15.547,11.617 23.33,17.605 8.6,6.614 20.221,7.445 29.611,2.112 13.203,-7.5 27.312,-13.347 41.932,-17.377 10.395,-2.865 18.019,-11.667 19.422,-22.426 1.27,-9.731 2.631,-19.465 4.048,-28.933 0.209,-1.397 1.461,-2.452 2.911,-2.452 h 45.773 c 1.45,0 2.702,1.054 2.911,2.453 1.417,9.465 2.778,19.198 4.048,28.932 1.403,10.759 9.027,19.561 19.421,22.426 14.62,4.03 28.728,9.877 41.934,17.377 9.388,5.33 21.01,4.502 29.608,-2.114 7.783,-5.987 15.633,-11.91 23.329,-17.604 1.137,-0.842 2.769,-0.703 3.794,0.324 l 32.366,32.366 c 1.026,1.026 1.164,2.657 0.324,3.793 -5.705,7.714 -11.628,15.562 -17.604,23.33 -6.615,8.601 -7.445,20.223 -2.112,29.612 7.501,13.205 13.347,27.313 17.377,41.933 2.865,10.394 11.669,18.016 22.424,19.418 9.716,1.268 19.451,2.63 28.934,4.048 1.399,0.21 2.453,1.461 2.453,2.911 v 45.773 z" + id="path2" + inkscape:connector-curvature="0" /> + </g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g12"> + <g + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g10"> + <path + inkscape:connector-curvature="0" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path8" + d="m 256,144.866 c -61.28,0 -111.134,49.854 -111.134,111.134 0,61.28 49.854,111.134 111.134,111.134 61.28,0 111.134,-49.854 111.134,-111.134 0,-61.28 -49.854,-111.134 -111.134,-111.134 z m 0,198.359 c -48.097,0 -87.225,-39.129 -87.225,-87.225 0,-48.097 39.13,-87.225 87.225,-87.225 48.096,0 87.225,39.129 87.225,87.225 0,48.096 -39.128,87.225 -87.225,87.225 z" /> + </g> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g14"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g16"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g18"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g20"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g22"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g24"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g26"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g28"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g30"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g32"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g34"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g36"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g38"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g40"> +</g><g + transform="translate(5,5)" + style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="g42"> +</g></g> +</svg>
\ No newline at end of file |